基本資料結構--單連結清單的實作。并嘗試實作一種重要操作:将給定連結清單反序。
基本資料結構實作--單連結清單
連結清單是線性表的另一種實作方式。與順序表不同,連結清單中邏輯上相鄰的資料元素在實體上未必相鄰,而是通過一個指針指明下一個元素的
實體位址。單連結清單中節點類型的描述如下:
1 struct LNode {
2 ElemType data; //節點資料域
3 LNode* next; //指向下一個節點的指針
4 };
5 typedef LNode* LinkList;
單連結清單的優點:與順序表相比,單連結清單的優點在于插入和删除操作:雖然單連結清單的插入和删除操作與順序表一樣時間複雜度都是O(n),但是對于單連結清單來說,
插入和删除時的基本操作主要是“比較”(對于按位插入删除,比較是否到了第i位;對于按值操作,比較值是否相等),而順序表插入和删除時
的基本操作主要是“移動”。從硬體的角度講,做一次比較運算的時間是遠小于指派運算的。是以,連結清單的插入和删除操作要高效得多。
單連結清單的缺點:由于單連結清單中邏輯上相鄰的元素在實體上未必相鄰,故不能根據第一個元素的位址立即計算出第i個元素的位址。于是單連結清單不支援随機通路。
(所謂随機通路是指:根據起始位址加上元素序号就可以立即找到任意一個元素。)要在單連結清單中通路第i個元素,必須一個一個通路連結清單中每一個元素直到
第i個。
以下代碼實作一個帶頭節點的單連結清單,并嘗試實作一種重要操作:将連結清單反序
1 /* 帶頭節點的單連結清單
2
3 實作操作:
4 *1 判空
5 *2 求表長
6 *3 按位序查找
7 *4 按值查找
8 *5 前插操作
9 *6 後插操作
10 *7 按位插入
11 *8 删除指定節點
12 *9 按位删除
13 *10 單連結清單的建立-頭插和尾插
14 */
15 #include <iostream>
16 #include <cstdio>
17 using namespace std;
18
19 typedef int ElemType;
20 typedef struct LNode {
21 ElemType data; //節點資料域
22 LNode* next; //指向下一個節點的指針
23 } * LinkList;
24
25 bool Empty(LinkList& L)
26 {
27 if( L->next == NULL )
28 return true;
29 return false;
30 }
31
32 //求表的長度
33 int Length(LinkList L)
34 {
35 int len = 0;
36 LNode* p = L;
37 //始終讓p指向第j個節點,當p沒有下一個節點時,j就是表的長度
38 while( p->next != NULL ) {
39 len++;
40 p = p->next;
41 }
42 return len;
43 }
44
45 //按位序查找,傳回指向第i個節點的指針
46 //查找失敗時傳回的是NULL
47 LNode* GetElem(LinkList L,int i)
48 {
49 if( i < 0 )
50 return NULL;
51 int j = 0;
52 LNode* p = L;
53 while( j < i && p != NULL ) {
54 j++;
55 p = p->next;
56 }
57 return p; //i=0時傳回的是頭節點,i值大于表長是傳回的是空指針
58 }
59
60 //按值查找,找到資料域為e的節點,傳回其指針
61 LNode* LocateElem(LinkList L,ElemType e)
62 {
63 LNode* p = L->next; //注意不是LNode* p = L :因為指向頭節點,資料域是未知的
64 while( p != NULL && p->data != e ) {
65 p = p->next;
66 }
67 return p; //若e不存在,傳回空指針
68 }
69
70 //前插操作:在p節點之前插入元素e
71 bool InsertPriorNode(LNode* p,ElemType e)
72 {
73 if( p == NULL )
74 return false;
75
76 //偷天換日
77 LNode* s = new LNode;
78 s->next = p->next;
79 p->next = s;
80 s->data = p->data;
81 p->data = e;
82 return true;
83 }
84
85 //後插操作:在p節點之後插入元素e
86 bool InsertNextNode(LNode* p,ElemType e)
87 {
88 if( p == NULL )
89 return false;
90 LNode* s = new LNode;
91 s->data = e;
92 s->next = p->next;
93 p->next = s;
94 return true;
95 }
96
97 //按位插入:在單連結清單L的第i個位置插入元素e
98 bool ListInsert(LinkList& L,int i,ElemType e)
99 {
100 if( i < 1 )
101 return false; //位序最小是1
102
103 //先拿到指向第i-1個節點的指針,然後在他後面插入元素e
104 LNode* p = GetElem(L,i-1);
105 return InsertNextNode(p,e);
106 }
107
108 //删除指定節點p
109 bool DeleteNode(LNode* p)
110 {
111 if( p == NULL )
112 return false;
113 //偷天換日
114 LNode* s = p->next;
115 //有bug:當p是最後一個節點時,無法删除(無法從p->next擷取data)
116 //非要删除最後一個節點的話,可以傳L頭節點指針進來
117 p->data = s->data;
118 p->next = s->next;
119 delete s;
120
121 return true;
122 }
123
124 //按位删除:删除單連結清單L的位序為i的元素,并用e傳回
125 bool ListDelete(LinkList& L, int i,int& e)
126 {
127 if( i < 1 )
128 return false;
129 LNode* p = GetElem(L,i-1);
130
131 if( p == NULL ) //沒有第i-1個節點,更沒有第i個節點
132 return false;
133 if( p->next == NULL ) //找到了第i-1個節點,但是沒有第i個節點
134 return false;
135 LNode* t = p->next;
136 e = t->data;
137 p->next = t->next;
138 delete t;
139 return true;
140 }
141
142 //頭插法建立單連結清單(假設所給資料從鍵盤輸入)
143 LinkList List_HeadInsert(LinkList& L)
144 {
145 //假設元素為int型
146 int x;
147 L = new LNode;
148 L->next = NULL;
149 while( cin >> x ) {
150 InsertNextNode(L,x);
151 }
152 return L;
153 }
154
155 //尾插法建立單連結清單(假設所給資料從鍵盤輸入)
156 LinkList List_TailInsert(LinkList& L)
157 {
158 //假設元素為int型
159 int x;
160 L = new LNode;
161 L->next = NULL;
162 LNode* r = L;
163 while( cin >> x ) {
164 LNode* s = new LNode;
165 s->data = x;
166 r->next = s;
167 r = s; //建立過程中不必将尾節點的next置空,因為後面還會有節點
168 }
169 r->next = NULL; //連結清單建立結束,最後一個節點的下一個節點置空
170 return L;
171 }
172
173 //按順序輸出單連結清單中所有元素
174 void Print(LinkList L)
175 {
176 if( L == NULL )
177 return ;
178 LNode* iterater = L->next;
179 while( iterater != NULL ) {
180 cout << iterater->data << endl;
181 iterater = iterater->next;
182 }
183 }
184
185 void test1()
186 {
187 //*測試使用a[i]=2*i的100個元素建立連結清單
188 int a[100];
189 for( int i=0; i<100; i++ ) {
190 a[i] = 2*i;
191 }
192 LinkList L = new LNode;
193 L->next = NULL;
194 //使用尾插法将以上數組插傳入連結表L
195 LNode* r = L;
196 for( int i=0; i<100; i++ ) {
197
198 LNode* s = new LNode;
199 s->data = a[i];
200 r->next = s;
201 r = s;
202 }
203 r->next = NULL;
204
205 //*測試在值為20的元素後插入元素985211
206 LNode* t = LocateElem(L,20); //按值查找
207 InsertNextNode(t,985211);
208
209 //*測試删除位序為15的元素(值應該是26)
210 LNode* tt = GetElem(L,15); //按位序查找
211 if( tt != NULL ) {
212 DeleteNode(tt);
213 }
214
215 //*測試按位序删除位序為100的元素
216 int ttt = 0;
217 if( ListDelete(L,100,ttt) )
218 cout << "按位序删除成功,删除的元素是:" << ttt << endl;
219 Print(L);
220 cout << "-------------------------------" << endl;
221
222
223 //下面嘗試将L反序 *****重要*****
224 //抓住連結清單中的第一個資料元素,一個一個往後進行頭插,表頭結點指針還是L
225 LNode* it = L->next;
226 L->next = NULL; //*****第一個資料已經抓住了;不加這句會導緻表尾自己指向自己(而非指向NULL)
227
228 //從連結清單的第一個節點開始,一個一個地在将表中所有節點重新插在表頭
229 while( it != NULL ) {
230 LNode* nxt = it->next;
231 it->next = L->next;
232 L->next = it; //頭插法重構連結清單,即可将連結清單反序
233 it = nxt;
234 }
235 Print(L);
236
237 //*測試查找不存在的元素傳回空指針
238 if( LocateElem(L,999) == NULL ) {
239 cout << endl << "999不存在" << endl;
240 }
241
242 }
243
244 //測試頭插法
245 void test2()
246 {
247 LinkList L;
248 List_HeadInsert(L);
249 Print(L);
250 }
251
252 int main()
253 {
254 // test1();
255 // test2();
256 return 0;
257 }