实现双链表

Posted mruptcw2

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了实现双链表相关的知识,希望对你有一定的参考价值。

双链表结构类型的定义

一个双链表的类型可以定义如下:

1 typedef struct DNode {
2 ElemType data; //节点数据域
3 DNode next, prior; //后继指针、前驱指针
4 } * DLinkList;
  此处,DNode 定义了一个双链表的节点类型,DLinkList便定义了一个双链表。DLinkList即是头结点指针。请注意:要标记一个链表,只需

要保存链表的头节点指针就可以了(当然根据需求不同也可以保存尾指针,甚至第i个节点的指针)。因此这里DLinkList既是双链表的头指针,又

代表了“双链表”这一数据类型。

*双链表基本操作及其实现

(1) 初始化一个双链表

复制代码
1 void InitDLinkList(DLinkList& L)
2 {
3 L = new DNode; //C语言版:L = (DNode*)malloc(sizeof(DNode));
4 L->data = 0;
5 L->next = NULL;
6 L->prior = NULL;
7 }
复制代码
由于L指向的是链表的头节点,这个数据域是不属于链表的有效元素的,如果没有他用,可以随意赋值(这里赋值0).

(2) 后插操作,在p指向的节点之后插入s指向的节点。插入成功返回true,失败(如p不合法等)返回false。

复制代码
1 bool InsertNextDNode( DNode p,DNode s )
2 {
3 if( p == NULL || s == NULL )
4 return false;
5 s->next = p->next;
6 //如果p是最后一个节点,p没有下一个节点,自然不需要修改下一个结点的前驱指针
7 if( p->next != NULL )
8 p->next->prior = s;
9 s->prior = p;
10 p->next = s;
11 return true;
12 }
复制代码
注意这里与单链表的不同。双链表在插入、删除操作时不仅要修改next指针,还要修改节点的prior指针。在后插时,

如果p是最后一个节点,则不需要修改p->next->prior:因为p没有next。其他情况时需要修改p->next->prior为s。

(3) 删除操作:删除p的后继节点。成功删除返回true,失败(如p不合法,或p没有后继节点等)返回false。

复制代码
1 bool DeleteNextDNode( DNode* p )
2 {
3 if( p == NULL )
4 return false;
5 if( p->next == NULL )
6 return false;
7 DNode* q = p->next; //q是p的后继节点
8 //需要修改两个指针:p的next以及q的后继的prior
9 if( q->next != NULL )
10 q->next->prior = p;
11 p->next = q->next;
12 delete q; //C语言版: free(q);
13 return true;
14 }
复制代码
要注意:p是否是最后一个节点?p的下一个节点是否是最后一个节点?

(4) 按值查找

复制代码
1 DNode* LocateElem( DLinkList DL,ElemType e )
2 {
3 DNode* p = DL->next;
4 while( p != NULL && p->data != e ) {
5 p = p->next;
6 }
7 return p;
8 }
复制代码

(5) 按位序查找

复制代码
1 DNode* GetElem( DLinkList L, int i )
2 {
3 if( i < 0 ) //认为头节点是第0个节点,如果输入i为0则返回头结点的指针
4 return NULL;
5 int j = 0;
6 DNode* p = L;
7 while( j != i && p != NULL ) {
8 j++;
9 p = p->next;
10 }
11 return p;
12 }
复制代码
按值查找与按位序查找在仅有头指针的前提下和单链表中的实现没有任何区别。

(6) 销毁整个链表

在实现了删除后继节点的操作后,我们就可以使用这个操作,逐一删除头节点的后继节点,从而达成销毁整个

链表的目的。请注意:把头结点看成第0个节点的话,每执行一次,相当于删除了链表中的第一个节点,而删

除前的第二个节点成为了新的第一个节点。实现代码如下:

复制代码
1 void DestroyList( DLinkList& DL )
2 {
3 while( DL->next != NULL )
4 DeleteNextDNode(DL);
5 delete DL;
6 DL = NULL;
7 }
复制代码

  • 代码测试

  以下测试先将0,2,4,6,8,10,12,14,16,18这10个数以后插的方式建立链表,然后尝试找到值为18的节点并

将其删除;接下来测试查找不存在的,值为200的节点。然后尝试用后插操作实现一个前插操作。(思想:

在p前插入s,先通过prior指针拿到p的前驱节点pp,这样,就相当于对pp执行后插操作)。

复制代码
1 /* 带头节点的双链表
2
3 实现操作:
4 *1 删除p的后继节点
5 *2 后插操作:在p后插入s
6 *3 前插操作:在p前插入s
7 *4 按值查找
8 *5 按位序查找
9 *6 销毁整个链表
10 */
11 #include <iostream>
12 #include <cstdio>
13 using namespace std;
14
15 typedef int ElemType;
16
17 typedef struct DNode {
18 ElemType data; //节点数据域
19 DNode next, prior; //后继指针、前驱指针
20 } * DLinkList;
21
22 //初始化一个双链表
23 void InitDLinkList(DLinkList& L)
24 {
25 L = new DNode; //C语言版:L = (DNode*)malloc(sizeof(DNode));
26 L->data = 0;
27 L->next = NULL;
28 L->prior = NULL;
29 }
30
31 //后插操作,在p指向的节点之后插入s指向的节点
32 bool InsertNextDNode( DNode p,DNode s )
33 {
34 if( p == NULL || s == NULL )
35 return false;
36 s->next = p->next;
37 //如果p是最后一个节点,p没有下一个节点,自然不需要修改下一个结点的前驱指针
38 if( p->next != NULL )
39 p->next->prior = s;
40 s->prior = p;
41 p->next = s;
42 return true;
43 }
44
45 //删除操作:删除p节点的后继节点
46 //由于链表是带头结点的,因此使用这个函数也可以删除表的第一个数据节点
47 bool DeleteNextDNode( DNode* p )
48 {
49 if( p == NULL )
50 return false;
51 if( p->next == NULL )
52 return false;
53 DNode* q = p->next; //q是p的后继节点
54 //需要修改两个指针:p的next以及q的后继的prior
55 if( q->next != NULL )
56 q->next->prior = p;
57 p->next = q->next;
58 delete q; //C语言版: free(q);
59 return true;
60 }
61
62 //销毁整个链表
63 void DestroyList( DLinkList& DL )
64 {
65 while( DL->next != NULL )
66 DeleteNextDNode(DL);
67 delete DL;
68 DL = NULL;
69 }
70
71 //打印整个链表
72 void Print( DLinkList DL )
73 {
74 if( DL == NULL )
75 return ;
76 DNode* p = DL->next;
77 while( p != NULL ) {
78 cout << p->data << endl;
79 p = p->next;
80 }
81
82 }
83
84 //按值查找:找到表中第一个值等于e的元素,返回其指针,如果
85 //不存在值等于e的,返回NULL
86 DNode* LocateElem( DLinkList DL,ElemType e )
87 {
88 DNode* p = DL->next;
89 while( p != NULL && p->data != e ) {
90 p = p->next;
91 }
92 return p;
93 }
94
95 //按位序查找,找到表中第i个元素,返回其指针
96 DNode* GetElem( DLinkList L, int i )
97 {
98 if( i < 0 ) //认为头节点是第0个节点,如果输入i为0则返回头结点的指针
99 return NULL;
100 int j = 0;
101 DNode* p = L;
102 while( j != i && p != NULL ) {
103 j++;
104 p = p->next;
105 }
106 return p;
107 }
108
109 //尝试使用后插操作实现一个前插操作:要在p指向的节点前插入s指向的节点,
110 //先拿到p的前驱节点的指针pp,在pp后插入s
111 bool InsertPriorNode( DNode p,DNode s )
112 {
113 if( p == NULL || s == NULL )
114 return false ;
115 //如果p的前驱为空,说明p是头节点,显然不能在头节点前插入数据
116 if( p->prior == NULL )
117 return false;
118 DNode* pp = p->prior;
119 InsertNextDNode(pp,s);
120 return true;
121 }
122
123
124 void test1()
125 {
126 //初始化
127 DLinkList DL;
128 InitDLinkList(DL);
129 //测试后插
130 for( int i=0; i<10; i++ ) {
131 DNode* temp = new DNode;
132 temp->data = 2*i;
133 temp->next = temp->prior = NULL;
134 InsertNextDNode(DL,temp);
135 }
136 Print(DL);
137 cout << "-------------------------" << endl;
138 //测试按值查找和删除
139 {
140 //尝试找到值为18的节点并将其删除
141 DNode* temp = LocateElem(DL,18);
142 if( temp != NULL )
143 DeleteNextDNode(temp);
144
145 //尝试查找不存在的节点
146 temp = LocateElem(DL,200);
147 if( temp == NULL ) {
148 cout << "200不存在" << endl;
149 }
150 }
151 Print(DL);
152 cout << "--------------------------" << endl;
153 //测试前插操作
154 {
155 //尝试在第一个元素及最后一个元素前插入值为985的元素
156 DNode* temp1 = GetElem(DL,1);
157 DNode* temp2 = GetElem(DL,9); //表中现在只有9个元素
158 cout << temp2->data << endl;
159 DNode* _9851 = new DNode;
160 _9851->data = 985;
161 _9851->next = _9851->prior = NULL;
162 DNode* _9852 = new DNode;
163 _9852->data = 985;
164 _9852->next = _9852->prior = NULL;
165 if( InsertPriorNode(temp1,_9851) && InsertPriorNode(temp2,_9852) )
166 Print(DL);
167 }
168 //测试销毁
169 {
170 DestroyList(DL);
171 cout << "123456" << endl;
172 Print(DL);
173 cout << "123456" << endl;
174 }
175 }
176
177 int main()
178 {
179 test1();
180 return 0;
181 }

以上是关于实现双链表的主要内容,如果未能解决你的问题,请参考以下文章

实现双链表

有序的双链表的实现

双链表的实现

数据结构[双链表的实现,以及双链表和单链表之间的比较,链表和顺序表的优劣]

基础数据结构---双链表go语言的代码实现

基础数据结构---双链表go语言的代码实现