当涉及到双向链表时,使用一个头结点(也称作哨兵节点)可以简化一些操作,比如插入和删除节点的操作。头结点是一个特殊的节点,它位于链表的开始位置,不存储任何实际数据,但使得链表的一些边界情况更容易处理。
下面我们将详细讨论如何实现一个带有头结点的双向链表。
1. 定义节点结构体
首先定义一个结构体来表示双向链表中的节点。
c
深色版本
1typedef struct Node {
2 int data; // 存储的数据
3 struct Node *next; // 指向下一个节点的指针
4 struct Node *prev; // 指向前一个节点的指针
5} Node;
2. 初始化链表
定义一个函数来初始化链表,即创建一个空的头结点。
c
深色版本
1Node* initList() {
2 Node* head = (Node*)malloc(sizeof(Node));
3 if (head == NULL) {
4 printf("Memory allocation failed\n");
5 return NULL;
6 }
7 head->data = 0; // 可以设置为任意值,因为不会被使用
8 head->next = NULL;
9 head->prev = NULL;
10 return head;
11}
3. 在链表头部插入节点
定义一个函数来在链表的头部插入一个新节点。
c
深色版本
1void insertAtHead(Node* head, int value) {
2 Node* newNode = (Node*)malloc(sizeof(Node));
3 if (newNode == NULL) {
4 printf("Memory allocation failed\n");
5 return;
6 }
7 newNode->data = value;
8 newNode->next = head->next;
9 newNode->prev = head;
10
11 if (head->next != NULL) {
12 head->next->prev = newNode;
13 }
14 head->next = newNode;
15}
4. 在链表尾部插入节点
定义一个函数来在链表的尾部插入一个新节点。
c
深色版本
1void insertAtTail(Node* head, int value) {
2 Node* newNode = (Node*)malloc(sizeof(Node));
3 if (newNode == NULL) {
4 printf("Memory allocation failed\n");
5 return;
6 }
7 newNode->data = value;
8 newNode->next = NULL;
9 newNode->prev = head->prev;
10
11 if (head->prev != NULL) {
12 head->prev->next = newNode;
13 }
14 head->prev = newNode;
15}
5. 删除链表中的节点
定义一个函数来删除链表中的指定节点。
c
深色版本
1void deleteNode(Node* head, Node* nodeToDelete) {
2 if (nodeToDelete == NULL) return;
3
4 if (nodeToDelete->prev != NULL)
5 nodeToDelete->prev->next = nodeToDelete->next;
6 else
7 head->next = nodeToDelete->next;
8
9 if (nodeToDelete->next != NULL)
10 nodeToDelete->next->prev = nodeToDelete->prev;
11 else
12 head->prev = nodeToDelete->prev;
13
14 free(nodeToDelete);
15}
6. 遍历链表
定义一个函数来遍历并打印链表中的所有节点。
c
深色版本
1void printList(Node* head) {
2 Node* temp = head->next;
3 while (temp != NULL) {
4 printf("%d ", temp->data);
5 temp = temp->next;
6 }
7 printf("\n");
8}
7. 示例程序
下面是一个完整的示例程序,演示如何使用上述定义的函数。
c
深色版本
1#include <stdio.h>
2#include <stdlib.h>
3
4// 之前定义的 Node 结构体和相关函数
5
6int main() {
7 Node* head = initList();
8
9 insertAtHead(head, 10);
10 insertAtHead(head, 20);
11 insertAtTail(head, 30);
12 insertAtTail(head, 40);
13
14 printf("Linked list after insertions: ");
15 printList(head);
16
17 deleteNode(head, head->next); // 删除第一个节点
18
19 printf("Linked list after deleting the first node: ");
20 printList(head);
21
22 return 0;
23}
注意事项
- 在实际应用中,通常会使用一个指针指向头结点,而不是指向真正的数据节点。
- 当删除链表中的最后一个节点时,需要更新头结点的
prev
指针。 - 当在链表头部插入节点时,需要确保更新头结点的
next
指针以及新节点的prev
指针。 - 当在链表尾部插入节点时,需要确保更新头结点的
prev
指针以及新节点的next
指针。
以上就是带有一个头结点的双向链表的基本实现。你可以在此基础上进一步扩展功能,例如实现反转链表、查找节点等功能。