双向链表概念
双向链表:它是一种数据结构,含有数据域以及含有指向前一个结点的指针和指向后一个结点的指针。
在图中我们发现head指向的是data,而tail指向的是最后一个data,与单链表对比,就会发现双向链表多了一个tail,指向最后一个结点。
双向链表的特点
头节点(Head):指向第一个数据节点
尾节点(Tail):指向最后一个数据节点
除去头节点和尾节点每个节点都有两个指针
双向遍历:可以从头结点向后遍历到尾节点,也可以从尾结点向前遍历到头节点
这种结构在删除和插入操作中更为灵活,因为不需要向单链表那样,只能从头结点开始单项遍历寻找前一个节点。
双向链表的建立、插入、删除、打印
// 定义节点结构体
typedef struct Node {
int data;
struct Node* prev;
struct Node* next;
} Node;
创建新节点
// 创建新节点
Node* createNode(int data) {
Node* newNode = (Node*)malloc(sizeof(Node));
newNode->data = data;
newNode->prev = NULL;
newNode->next = NULL;
return newNode;
}
在头部插入节点
// 在头部插入节点
void insertAtHead(Node** head, int data) {
Node* newNode = createNode(data); // 创建新节点
if (*head == NULL) { // 如果链表为空
*head = newNode; // 新节点成为头节点
} else {
newNode->next = *head; // 新节点的后继指针指向当前头节点
(*head)->prev = newNode; // 当前头节点的前驱指针指向新节点
*head = newNode; // 更新头节点为新节点
}
}
在尾部插入节点
// 在尾部插入节点
void insertAtTail(Node** head, int data) {
Node* newNode = createNode(data); // 创建新节点
if (*head == NULL) { // 如果链表为空
*head = newNode; // 新节点成为头节点
} else {
Node* temp = *head; // 临时指针指向头节点
while (temp->next != NULL) { // 遍历到链表尾部
temp = temp->next;
}
temp->next = newNode; // 尾节点的后继指针指向新节点
newNode->prev = temp; // 新节点的前驱指针指向尾节点
}
}
删除节点
// 删除节点
void deleteNode(Node** head, Node* delNode) {
if (*head == NULL || delNode == NULL) { // 如果链表为空或要删除的节点为空
return; // 直接返回
}
if (*head == delNode) { // 如果要删除的节点是头节点
*head = delNode->next; // 更新头节点为下一个节点
}
if (delNode->next != NULL) { // 如果要删除的节点有后继节点
delNode->next->prev = delNode->prev; // 后继节点的前驱指针指向删除节点的前驱节点
}
if (delNode->prev != NULL) { // 如果要删除的节点有前驱节点
delNode->prev->next = delNode->next; // 前驱节点的后继指针指向删除节点的后继节点
}
free(delNode); // 释放删除节点的内存
}
打印链表
void printList(Node* head) {
Node* temp = head; // 临时指针指向头节点
while (temp != NULL) { // 遍历链表
printf("%d ", temp->data); // 打印节点数据
temp = temp->next; // 移动到下一个节点
}
printf("\n");
}
主函数
int main() {
Node* head = NULL; // 初始化头节点为空
insertAtHead(&head, 1); // 在头部插入节点1
insertAtHead(&head, 2); // 在头部插入节点2
insertAtHead(&head, 3); // 在头部插入节点3
insertAtTail(&head, 4); // 在尾部插入节点4
insertAtTail(&head, 5); // 在尾部插入节点5
printf("双向链表: ");
printList(head); // 打印链表
// 删除头节点
deleteNode(&head, head); // 删除头节点
printf("删除头节点后: ");
printList(head); // 打印链表
// 删除尾节点
Node* tail = head; // 临时指针指向头节点
while (tail->next != NULL) { // 遍历到尾节点
tail = tail->next;
}
deleteNode(&head, tail); // 删除尾节点
printf("删除尾节点后: ");
printList(head); // 打印链表
return 0;
}