双向 (循环)链表. 整个链表只能单方向从表头访问到表尾,这种结构的链表统称为 “单向链表”或“单链表”。. 如果算法中需要频繁地找某结点的前趋结点,单链表的解决方式是遍历整个链表,增加算法的时间复杂度,影响整体效率。. 为了快速便捷地解决这类问题,在单向链表的基础上,给各个结点额外配备一个指针变量,用于指向每个结点的直接前趋元素。. 这样的链表被称为“双向链表”或者“双链表”。.
循环双链表与循环单链表类似, 只是在某些操作上有所不同.
首先是定义, 这里的命名为:
pre: 前驱元素
next:后继元素
由此, 对于单向链表的尾指针, 相当于双向链表头结点的前驱元素
typedef struct ListNode{
ListNode *pre, *next;
int val;
}ListNode;
结点和链表的初始化:
ListNode *initListNode(int val){
ListNode *node = (ListNode *)malloc(sizeof(ListNode));
node->pre = node->next = NULL;
node->val = val;
return node;
}
LinkList *initLinkList(){
LinkList *list = (LinkList *)malloc(sizeof(LinkList));
list->head.val = 0;
list->head.pre = list->head.next = &(list->head);
return list;
}
这里list->head.pre = list->head.next = &(list->head)的原因与单向循环链表的tail及head.next是一样的, 与下面函数的操作有很大关联.
链表的清空与单链表有所不同(单向循环链表里面忘记提及,这里补一下):
void clearLinkList(LinkList *list){
if(list == NULL) return;
ListNode *p = list->head.next, *q;
while(p != &(list->head)){
q = p->next;
clearListNode(p);
p = q;
}
free(list);
return;
}
这里将clear相当于destroy,想要分别实现两种操作请参考单向链表;
由于list->head.val维护了list的长度, 而双向链表可以有两种遍历方向, 于是可以考虑根据操作下标在链表中的位置来优化时间↓↓↓
插入和删除: 内部操作与普通双链表一致, 但是带有位置优化,使得期望时间复杂度低于O(n)
bool insert(LinkList *list, int ind, int val){
if(list == NULL || ind < 0 || ind > list->head.val) return false;
//考虑一下双向链表对于插入与删除的优化;
ListNode *node = initListNode(val), *p = &(list->head);
if(ind > (list->head.val >> 1)){
ind = list->head.val + 1 - ind;
while(ind--) p = p->pre;
node->pre = p->pre;
node->next = p;
p->pre->next = node;
p->pre = node;
}else{
while(ind--) p = p->next;
node->next = p->next;
node->pre = p;
p->next->pre = node;
p->next = node;
}
list->head.val++;
return true;
}
删除:
bool erase(LinkList *list, int ind){
if(list == NULL || ind < 0 || ind >= list->head.val) return false;
ListNode *p = &(list->head), *q;
if(ind > (list->head.val >> 1)){
ind = list->head.val + 1 - ind;
while(ind--) p = p->pre;
q = p->pre->pre;
clearListNode(p->pre);
p->pre = q;
q->next = p;
}else{
while(ind--) p = p->next;
q = p->next->next;
clearListNode(p->next);
p->next = q;
q->pre = p;
}
list->head.val--;
return true;
}
由于双向链表的两个方向, 所以遍历方式也有两种: ordered顺序遍历, reverse逆序遍历
void orderedOutput(LinkList *list){
if(list == NULL) return;
puts("Ordered output:");
printf("LinkList(%d): head-> ", list->head.val);
for(ListNode *p = list->head.next; p != &(list->head); p = p->next){
printf("%d-> ", p->val);
}
puts("head\n");
return;
}
void reverseOutput(LinkList *list){
if(list == NULL) return;
puts("Reverse output:");
printf("LinkList(%d): head<- ", list->head.val);
for(ListNode *p = list->head.pre; p != &(list->head); p = p->pre){
printf("%d<- ", p->val);
}
puts("head\n");
return;
}
下面是双向循环链表的完整代码:(带DEBUG)
#include<bits/stdc++.h>
using namespace std;
typedef struct ListNode{
ListNode *pre, *next;
int val;
}ListNode;
typedef struct LinkList{
ListNode head;
}LinkList;
ListNode *initListNode(int val){
ListNode *node = (ListNode *)malloc(sizeof(ListNode));
node->pre = node->next = NULL;
node->val = val;
return node;
}
LinkList *initLinkList(){
LinkList *list = (LinkList *)malloc(sizeof(LinkList));
list->head.val = 0;
list->head.pre = list->head.next = &(list->head);
return list;
}
void clearListNode(ListNode *node){
if(node == NULL) return;
free(node);
return;
}
void clearLinkList(LinkList *list){
if(list == NULL) return;
ListNode *p = list->head.next, *q;
while(p != &(list->head)){
q = p->next;
clearListNode(p);
p = q;
}
free(list);
return;
}
bool insert(LinkList *list, int ind, int val){
if(list == NULL || ind < 0 || ind > list->head.val) return false;
//考虑一下双向链表对于插入与删除的优化;
ListNode *node = initListNode(val), *p = &(list->head);
if(ind > (list->head.val >> 1)){
ind = list->head.val + 1 - ind;
while(ind--) p = p->pre;
node->pre = p->pre;
node->next = p;
p->pre->next = node;
p->pre = node;
}else{
while(ind--) p = p->next;
node->next = p->next;
node->pre = p;
p->next->pre = node;
p->next = node;
}
list->head.val++;
return true;
}
bool erase(LinkList *list, int ind){
if(list == NULL || ind < 0 || ind >= list->head.val) return false;
ListNode *p = &(list->head), *q;
if(ind > (list->head.val >> 1)){
ind = list->head.val + 1 - ind;
while(ind--) p = p->pre;
q = p->pre->pre;
clearListNode(p->pre);
p->pre = q;
q->next = p;
}else{
while(ind--) p = p->next;
q = p->next->next;
clearListNode(p->next);
p->next = q;
q->pre = p;
}
list->head.val--;
return true;
}
void orderedOutput(LinkList *list){
if(list == NULL) return;
puts("Ordered output:");
printf("LinkList(%d): head-> ", list->head.val);
for(ListNode *p = list->head.next; p != &(list->head); p = p->next){
printf("%d-> ", p->val);
}
puts("head\n");
return;
}
void reverseOutput(LinkList *list){
if(list == NULL) return;
puts("Reverse output:");
printf("LinkList(%d): head<- ", list->head.val);
for(ListNode *p = list->head.pre; p != &(list->head); p = p->pre){
printf("%d<- ", p->val);
}
puts("head\n");
return;
}
int main(){
#define MAX_N 1010 //可以在这里修改测试的数据量
srand(time(0));
LinkList *list = initLinkList();
for(int i = 0; i < MAX_N; i++){
int op = rand() % 6, val = rand() * rand() % 10000000, ind = rand() % (list->head.val + 1);
switch(op){
case 0:
if(erase(list, ind)) printf("Erase elem at %d from the list\n", ind);
else puts("Failed to erase elem from the list");
break;
case 1:
case 2:
case 3:
case 4:
case 5:
if(insert(list, ind, val)) printf("Insert %d at %d to the list\n", val, ind);
else puts("Failed to insert elem to the list");
}
orderedOutput(list);
reverseOutput(list);
}
clearLinkList(list);
printf("%d", list->head.val); //RE操作(逃
return 0;
}