1. 程式人生 > 其它 >基本資料結構實現--單鏈表【含測試程式碼】

基本資料結構實現--單鏈表【含測試程式碼】

基本資料結構實現--單鏈表

連結串列是線性表的另一種實現方式。與順序表不同,連結串列中邏輯上相鄰的資料元素在物理上未必相鄰,而是通過一個指標指明下一個元素的

實體地址。單鏈表中節點型別的描述如下:

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 }