循环双向链表
循环双向链表是循环链表和双向链表的组合。它的两个节点由上一个和下一个指针连接。最后一个节点的下一个指针指向第一个节点,第一个节点的上一个指针指向最后一个节点。它可以从两个方向遍历,也可以从尾巴到头部或从头到尾跳跃。它还用于实现高级数据结构,如斐波那契堆。
循环双向链表遍历
我们只需检查迭代中的下一个节点不是 head 的条件,然后遍历循环双向链表,然后根据向前/向后将 temp 从 temp->next 或 temp->prev 中移出迭代。
正向
令 head 成为链接列表的第一个节点。
-
初始化指向链接列表的
head节点的curr。 -
尽管
curr尚未到达列表的末尾,即curr->next!=head,请执行以下操作:- 打印存储在当前节点内的数据。
curr=curr->next;
同样,我们可以通过从 tail 开始并将 curr 更改为 curr->prev 来进行向后遍历。
反向
令 tail(即 head->prev)成为链接列表的最后一个节点。
-
初始化
curr,指向链接列表的tail节点。 -
尽管
curr尚未到达列表的开头,即curr->prev!=tail,请执行以下操作:- 打印存储在当前节点内的数据。
curr = curr->上一个
循环双向链表插入
有 3 种不同的插入情况。
在循环双向链表的开头插入元素
-
用给定的数据创建一个节点
temp。 -
将
temp->next设置为head,将temp->prev设置为tail,以便在head和tail之间插入temp。 -
将
tail->next和head->prev设置为temp以完成插入。 -
将
head设置为temp,以将 head 移至新插入的元素。
在循环双向链表的末尾插入一个元素
-
用给定的数据创建一个节点
temp。 -
如果列表为空,则使用给定数据创建节点
temp,并将temp->prev和temp->next指向其自身,并将其设置为head并返回。 -
执行开始时要插入的步骤,最后一步除外。
在循环双向链表的中间插入元素
令 X 为要在其后插入元素的节点的值。
-
用给定的数据创建一个节点
temp。 -
初始化指向
head的指针curr。 -
迭代直到找到带有
数据=X的节点。将其下一个节点存储为下一个。 -
将
curr->next设置为temp。 -
将
temp->prev设置为curr,将temp->next设置为next。 -
最后,将
下一个->上一个设置为温度。
循环双向链表插入插图
在循环双向链表的开头插入元素
在循环双向链表的末尾插入一个元素
在循环双向链表的中间插入元素
循环双向链表遍历和插入实现
#include <bits/stdc++.h> using namespace std; class Node { public: int data; Node* next; Node* prev; }; void insertEnd(Node** head, int value) { if (*head == NULL) { Node* temp = new Node; temp->data = value; temp->next = temp->prev = temp; *head = temp; return; } Node* tail = (*head)->prev; Node* temp = new Node; temp->data = value; temp->next = *head; (*head)->prev = temp; temp->prev = tail; tail->next = temp; } void insertBegin(Node** head, int value) { Node* tail = (*head)->prev; Node* temp = new Node; temp->data = value; temp->next = *head; temp->prev = tail; tail->next = (*head)->prev = temp; *head = temp; } void insertAfter(Node** head, int value1, int value2) { Node* temp = new Node; temp->data = value1; Node* curr = *head; while (curr->data != value2) curr = curr->next; Node* next = curr->next; curr->next = temp; temp->prev = curr; temp->next = next; next->prev = temp; } void printList(Node* head) { Node* curr = head; printf("Forward Traversal:"); while (curr->next != head) { printf("%d ", curr->data); curr = curr->next; } printf("%d ", curr->data); printf("\nBackward Traversal:"); Node* tail = head->prev; curr = tail; while (curr->prev != tail) { printf("%d ", curr->data); curr = curr->prev; } printf("%d ", curr->data); cout << "\n"; } int main() { Node* head = NULL; insertEnd(&head, 2); insertBegin(&head, 1); insertEnd(&head, 4); printList(head); insertEnd(&head, 5); insertAfter(&head, 3, 2); printList(head); return 0; } 循环双向链表遍历和插入算法的复杂性
遍历
时间复杂度
- 平均情况
要遍历完整的双向链表,我们必须访问每个节点。如果具有 n 个节点,则遍历的平均情况下时间复杂度约为 O(n)。时间复杂度约为 O(n)。
- 最佳情况
最佳情况下的时间复杂度是 O(n)。它与平均情况下的时间复杂度相同。
- 最坏情况
最差的时间复杂度是 O(n)。它与最佳情况下的时间复杂度相同。
空间复杂度
遍历算法的空间复杂度为 O(1),因为除了 curr 指针外不需要其他空间。
插入
时间复杂度
- 平均情况
要在 tail 和 head 处插入要插入的元素,最多需要更改 4 个链接,因此时间复杂度为 O(1)。但是要在两者之间插入,我们可能必须移动 n-1 个节点,因此时间复杂度为 O(n)。
- 最佳情况
对于所有 3 情况,最佳情况下的时间复杂度为 O(1)。
- 最坏情况
对于前两种情况,最坏情况下的时间复杂度为 O(1),对于第三种情况为 O(n)。它与平均情况下的时间复杂度相同。
空间复杂度
对于所有 3 种类型的插入,插入算法的空间复杂度为 O(1)。
Harshit Jindal has done his Bachelors in Computer Science Engineering(2021) from DTU. He has always been a problem solver and now turned that into his profession. Currently working at M365 Cloud Security team(Torus) on Cloud Security Services and Datacenter Buildout Automation.
LinkedIn