接上文,接下来我们分享双链表的概念及应用.
一.概念
双链表也称为双向链表,是一种常见的数据结构,它在链表的基础上增加了一个指向前一个节点的指针。
双链表的结构和单链表类似,每个节点都包含一个数据域和两个指针域,分别指向前一个节点和后一个节点。双链表的头节点只有一个指针域,指向第一个节点;尾节点的指针域指向NULL。
双链表可以比喻为一列多层的火车车厢,每个车厢都有前后两个连接到其他车厢的车门。通过这样的连接,我们可以在双链表中自由地在前后方向上移动。
二.优势
与单链表相比,双链表具有以下优点:
1. 可以双向遍历:由于每个节点都有指向前一个节点的指针,所以可以从任意节点开始,分别向前和向后遍历整个链表。
2. 删除和插入操作更加高效:在单链表中,删除一个节点需要找到它的前一个节点,而在双链表中只需要修改前后节点的指针即可。
下面是一个双链表的示例:
struct Node {
int data;
struct Node* prev;
struct Node* next;
};
struct Node* head; // 双链表的头节点
// 在链表尾部插入一个节点
void insertAtEnd(int data) {
struct Node* newNode = (struct Node*)malloc(sizeof(struct Node));
newNode->data = data;
newNode->next = NULL;
if (head == NULL) {
newNode->prev = NULL;
head = newNode;
return;
}
struct Node* current = head;
while (current->next != NULL) {
current = current->next;
}
current->next = newNode;
newNode->prev = current;
}
// 从链表中删除一个节点
void deleteNode(int data) {
struct Node* current = head;
while (current != NULL) {
if (current->data == data) {
if (current->prev == NULL) {
// 删除的是头节点
head = current->next;
if (head != NULL) {
head->prev = NULL;
}
} else {
current->prev->next = current->next;
if (current->next != NULL) {
current->next->prev = current->prev;
}
}
free(current);
return;
}
current = current->next;
}
}
// 正向遍历链表
void printForward() {
struct Node* current = head;
while (current != NULL) {
printf("%d ", current->data);
current = current->next;
}
}
// 反向遍历链表
void printBackward() {
struct Node* current = head;
while (current->next != NULL) {
current = current->next;
}
while (current != NULL) {
printf("%d ", current->data);
current = current->prev;
}
}
int main() {
head = NULL;
insertAtEnd(1);
insertAtEnd(2);
insertAtEnd(3);
printForward(); // 输出:1 2 3
printf("\n");
printBackward(); // 输出:3 2 1
printf("\n");
deleteNode(2);
printForward(); // 输出:1 3
printf("\n");
printBackward(); // 输出:3 1
printf("\n");
return 0;
}
在这个示例中,我们定义了一个双链表的结构体`Node`,包含数据域`data`和两个指针域`prev`和`next`。我们还定义了一个头节点`head`,它指向双链表的第一个节点。
我们实现了三个基本操作:
1.在链表尾部插入一个节点`insertAtEnd`
2.从链表中删除一个节点`deleteNode`
3.正向遍历链表`printForward`和反向遍历链表`printBackward`。
在`insertAtEnd`中,我们创建一个新节点,将它的数据域设置为给定的值,并将它插入到链表的尾部。如果链表为空,我们将头节点指向新节点;否则,我们遍历到链表的最后一个节点,将最后节点的指针域指向新节点。
在`deleteNode`中,我们遍历链表,寻找与给定值相等的节点。如果找到了,我们需要分情况讨论:1.如果要删除的节点是头节点,我们将头节点指向下一个节点,并将下一个节点的前驱指针设置为NULL;
2.若不是,我们则将要删除节点的前驱节点的指针域指向要删除节点的后继节点,并将后继节点的前驱指针设置为要删除节点的前驱节点。
在`printForward`中,我们从头节点开始遍历链表,依次输出每个节点的数据域。
在`printBackward`中,我们先从头节点开始遍历链表,直到最后一个节点,然后再从最后一个节点开始遍历链表,依次输出每个节点的数据域。
最后,我们在`main`函数中测试了这些操作。我们先插入三个节点1、2、3,然后分别进行正向和反向的遍历输出。接着,我们删除节点2,并再次进行正向和反向的遍历输出。
总结:
双链表是一种更为灵活的数据结构,在单链表的基础上增加了对删除和插入操作的高效支持。通过在每个节点中增加一个指向前一个节点的指针,双链表可以在常数时间内删除和插入节点。最重要的是,双链表还可以双向遍历,方便对链表进行操作和访问。
今日分享到此结束,期待下次再见!