双链表
廖家龙 用心听,不照做

单链表:无法逆向检索,有时候不太方便
双链表:可进可退,但存储密度要更低一点

双链表的初始化、插入、删除(带头结点):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
#include<stdio.h>
#include<stdbool.h>
#include<stdlib.h>

typedef struct DNode{
int data; //数据域
struct DNode *prior,*next; //指针域
}DNode,*DLinklist; //DLinklist等价于DNode *

//初始化双链表
bool InitDLinkList(DLinklist *L){
(*L)=(DNode *)malloc(sizeof(DNode)); //分配一个头结点
if((*L) == NULL){ //内存不足,分配失败
return false;
}

(*L)->prior=NULL; //头结点的prior永远指向NULL
(*L)->next=NULL; //头结点之后暂时还没有结点
return true;
}

//判断双链表是否为空(带头结点)
bool Empty(DLinklist *L){
if((*L)->next == NULL){
return true;
}
else{
return false;
}
}

//双链表的插入
//在p结点之后插入s结点
//如果要按位序插入,只需要从头结点找到这个位序的前驱结点,然后对这个前驱结点进行后插操作
//前插操作也是找到该结点的前驱结点,然后对这个前驱结点进行后插操作
bool InsertNextDNode(DNode *p,DNode *s){
if(p == NULL || s == NULL){ //非法参数
return false;
}

s->next=p->next; //将结点*s插入到结点*p之后
if(p->next != NULL){ //如果p结点有后继结点[如果是循环双链表,没有这个条件判断也是正确的]
p->next->prior=s;
}
s->prior=p;
p->next=s;
return true;
}

//双链表的删除
//删除p结点的后继结点
bool DeleteNextDNode(DNode *p){
if(p == NULL){
return false;
}
DNode *q=p->next; //找到p的后继结点q
if(q == NULL){
return false; //p没有后继
}

p->next=q->next;
if(q->next != NULL){ //q结点不是最后一个结点
q->next->prior=p;
}
free(q); //释放结点空间
return true;
}

//销毁一个双链表
void DestoryList(DLinklist *L){
//循环释放各个数据结点
while((*L)->next != NULL){
DeleteNextDNode(*L);
}
free(L); //释放头结点
L=NULL; //头指针指向NULL
}

int main(){

DLinklist L;
InitDLinkList(&L); //初始化双链表

//...后续代码
}

双链表的遍历[双链表不可随机存取,按位查找,按值查找操作都只能用遍历的方式实现,时间复杂度O(n)]

1
2
3
4
5
6
7
8
9
10
11
12
//后向遍历
while(p != NULL){
p=p->next;
}
//前向遍历
while(p != NULL){
p=p->prior;
}
//前向遍历,跳过头结点
while(p->prior != NULL){
p=p->prior;
}