双链表(Double LinkList)
在单链表中, 无法逆向检索, 有时候不太方便, 于是有了双链表.
单链表只包含指向后继结点的指针, 双链表则是在单链表的基础上增加了一个指针域prior
用于指向前驱结点.
0x01 双链表的定义
1 2 3 4
| typedef struct DNode { int data; struct DNode* prior, * next; }DNode, * DLinklist;
|
或者:
1 2 3 4 5 6
| struct DNode { int data; struct DNode* prior, * next; }; typedef struct DNode Dnode; typedef struct DNode * DLinklist;
|
两者等价.
0x02 双链表的初始化
双链表中有前驱指针
和后继指针
, 初始化一个双链表时前驱指针和后继指针所指向的都是NULL
.
双链表头指针的前驱指针
和末尾结点的后继指针
所指向的永远为NULL
.
1 2 3 4 5 6 7 8 9 10 11 12
|
bool InitDLinkList(DLinklist& L) { L = (DNode*)malloc(sizeof(DNode)); if (L == NULL) return false; L->prior = NULL; L->next = NULL; return true; }
|
0x03 双链表的插入
以下代码演示的是双链表的后插操作, 但实际上只需要用后插操作就能完成双链表的一系列插入操作,
例如你将在某一个结点处实现前插操作时只需要在该结点的前一个结点(双链表很容易找到前驱结点)使用后插操作即可.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
|
bool InsertNextDNode(DNode* p, DNode* s) { if (p == NULL || s == NULL) return false; s->next = p->next; if (p->next != NULL) p->next->prior = s; s->prior = p; p->next = s; return true; }
|
按位序插入可根据单链表的代码进行, 原理相同, 不再演示. (不是懒😤
0x04 双链表的删除
删除双链表其中一个结点的实现代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
|
bool DeleteDNode(DNode*& p) { if (p->next) { p->prior->next = p->next; p->next->prior = p->prior; } else { p->prior->next = NULL; } free(p); return true; }
|
删除双链表其中一个结点的后继结点:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
|
bool DeleteNextDNode(DNode* p) { if (p == NULL) return false; DNode* q = p->next; if (q == NULL) return false; p->next = q->next; if (q->next != NULL) q->next->prior = p; free(q); return true; }
|
彻底释放(销毁)一个双链表:
1 2 3 4 5 6 7 8 9 10 11 12
|
void DestroyList(DLinklist& L) { while (L->next != NULL) DeleteNextDNode(L); free(L); L = NULL; }
|
0x05 双链表的遍历
下方注释中的//···对结点p做相遇处理的代码行···
指在次可以添加一些操作如打印、计数等.
后向遍历:
1 2 3 4
| while( p != NULL){ p = p->next; }
|
前向遍历:
1 2 3 4
| while( p != NULL){ p = p->prior; }
|
向前遍历(跳过头结点):
1 2 3 4
| while( p->prior !=NULL){ p = p->prior; }
|
谢谢观看 😘