靜態鏈表、循環鏈表、雙向鏈表(C代碼實現)
靜態鏈表
對於沒有指針的編程語言,可以用數組替代指針,來描述鏈表。讓數組的每個元素由data和cur兩部分組成,其中cur相當於鏈表的next指針,這種用數組描述的鏈表叫做靜態鏈表,這種描述方法叫做遊標實現法。我們對數組的第一個和最後一個元素做特殊處理,不存數據。讓數組的第一個元素cur存放第一個備用元素(第一個未被占用的元素)下標,而數組的最後一個元素cur存放第一個有值的元素下標,相當於頭結點作用。空的靜態鏈表如下圖
當存放入一些數據時("甲""乙""丁""戊""己""庚"),靜態鏈表為:
靜態鏈表的插入操作
在靜態鏈表第三個位置插入"丙"後,結果如下:
靜態鏈表中要解決的是:如何用靜態模擬動態鏈表結構的存儲空間的分配,需要時申請,無用時釋放。
其主要的思想:(以我們如果要在乙 和 丁 中間插入一個丙 )
我們自己定義一個內存分配函數,如Malloc_SLL(StaticLinkList space)
1 //插入元素時,分配空間的下標(從備用鏈表中去取出)
2 int Malloc(StaticLinkList L)
3 {
4 int i = L[0].cur; //獲取備用鏈表的下表
5 if (i) //鏈表不為空
6 L[0 ].cur = L[i].cur; //將第一位的遊標改成備用鏈表的下表+1
7 return i;
8 }
然後再修改遊標表示,如上圖
代碼附上:
1 //靜態鏈表中i位置插入一個元素
2 bool StaticLinkListInsert(StaticLinkList L, int i, int key)
3 {
4 //判斷插入點是否合理
5 if (i<1 || i>StaticLinkListLength(L)+1)
6 {
7 return false;
8 }
9 int j = Malloc(L);
10 int k = MAXSIZE - 1;
11 if (j)
12 {
13 for (int l = 1; l <= j - 1; l++)
14 {
15 k = L[k].cur;
16 }
17 L[j].data = key;
18 L[j].cur = L[k].cur;
19 L[k].cur = j;
20 return true;
21 }
22 return false;
23
24 }
靜態鏈表的刪除操作
刪除操作其實就是插入操作的逆操作
我們同樣要自己定義一個Free函數
void Free(StaticLinkList L, int k)
{
L[k].cur = L[0].cur;
L[0].cur = k;
}
獲取要刪除的元素下標後,開始修改遊標
1 bool StaticLinkListDelete(StaticLinkList L,int i, int *key)
2 {
3 if (i < 1 || i >= StaticLinkListLength(L))
4 {
5 return false;
6 }
7 int k = MAXSIZE - 1;
8 for (int l = 1; l <= i-1; l++)
9 {
10 k = L[k].cur;
11 }
12 int j = L[k].cur;
13 *key = L[j].data;
14 L[k].cur = L[j].cur;
15 Free(L, j);
16 return true;
17 }
刪除"甲"後,靜態鏈表如下:
總結一下靜態鏈表的優缺點:
優點:在插入和刪除時,只需要修改遊標,不需要移動元素。從而改進了在順序存儲結構中的插入和刪除需要移動大量元素的特點。
缺點:沒有解決連續存儲分配帶來的表長難以確定的問題;失去了順序存儲結構隨機存儲的特性。
Copy 了大佬一些些文字和圖片:
https://www.cnblogs.com/zhaoxy/p/7754906.html
靜態鏈表實現的一些基本操作:(附上代碼)
1 #include<stdio.h> 2 3 #define MAXSIZE 7 4 #define true 1 5 #define false 0 6 7 typedef int bool; 8 9 typedef struct 10 { 11 int data; 12 int cur; 13 }StaticLinkList[MAXSIZE]; 14 15 //初始化靜態鏈表,0位置cur指向1位置,1位置cur指向2位置...MAXSIZE-1位置指向0位置 16 void InitStaticLinkList(StaticLinkList L)//本來想用小寫l的,但是小寫l看起來太怪了 17 { 18 for (int i = 0; i < MAXSIZE - 2; i++) 19 { 20 L[i].cur = i + 1;//最後一個元素指向的是尾節點 21 } 22 L[MAXSIZE - 2].cur = 0;//備用鏈表的最後一個空元素的cur指向0,這一行不能省。 23 L[MAXSIZE - 1].cur = 0; 24 } 25 26 27 //求靜態鏈表中元素個數,不包括頭尾節點 28 int StaticLinkListLength(StaticLinkList L) 29 { 30 int i = L[MAXSIZE - 1].cur; 31 int j = 0; 32 while (i) 33 { 34 j++; 35 i = L[i].cur; 36 } 37 return j; 38 } 39 40 //插入元素時,分配空間的下標 41 int Malloc(StaticLinkList L) 42 { 43 int i = L[0].cur; 44 if (i) 45 L[0].cur = L[i].cur; 46 return i; 47 } 48 49 50 //靜態鏈表中i位置插入一個元素 51 bool StaticLinkListInsert(StaticLinkList L, int i, int key) 52 { 53 //判斷插入點是否合理 54 if (i<1 || i>StaticLinkListLength(L)+1) 55 { 56 return false; 57 } 58 int j = Malloc(L); 59 int k = MAXSIZE - 1; 60 if (j) 61 { 62 for (int l = 1; l <= j - 1; l++) 63 { 64 k = L[k].cur; 65 } 66 L[j].data = key; 67 L[j].cur = L[k].cur; 68 L[k].cur = j; 69 return true; 70 } 71 return false; 72 73 } 74 75 76 void Free(StaticLinkList L, int k) 77 { 78 L[k].cur = L[0].cur; 79 L[0].cur = k; 80 } 81 82 //刪除第i個元素 83 84 bool StaticLinkListDelete(StaticLinkList L,int i, int *key) 85 { 86 if (i < 1 || i >= StaticLinkListLength(L)) 87 { 88 return false; 89 } 90 int k = MAXSIZE - 1; 91 for (int l = 1; l <= i-1; l++) 92 { 93 k = L[k].cur; 94 } 95 int j = L[k].cur; 96 *key = L[j].data; 97 L[k].cur = L[j].cur; 98 Free(L, j); 99 return true; 100 } 101 102 //遍歷 103 void StaticLinkListTraverse(StaticLinkList L) 104 { 105 int k = MAXSIZE - 1; 106 while (L[k].cur) 107 { 108 k = L[k].cur; 109 printf("%d ", L[k].data); 110 } 111 printf("\n"); 112 } 113 114 int main(void) 115 { 116 StaticLinkList L; 117 printf("初始化鏈表:\n"); 118 InitStaticLinkList(L); 119 printf("初始化鏈表之後,鏈表的長度為:%d\n", StaticLinkListLength(L)); 120 printf("插入1,2,3,4,5\n"); 121 StaticLinkListInsert(L, 1, 1); 122 StaticLinkListInsert(L, 1, 2); 123 StaticLinkListInsert(L, 1, 3); 124 StaticLinkListInsert(L, 1, 4); 125 StaticLinkListInsert(L, 1, 5); 126 printf("遍歷鏈表:\n"); 127 StaticLinkListTraverse(L); 128 printf("鏈表長度為:%d\n", StaticLinkListLength(L)); 129 printf("刪除第二個元素:\n"); 130 int key; 131 StaticLinkListDelete(L, 2, &key); 132 printf("刪除的元素值為:%d\n", key); 133 printf("遍歷鏈表:\n"); 134 StaticLinkListTraverse(L); 135 136 }View Code
並且附上代碼來源:
https://blog.csdn.net/hhhhhyyyyy8/article/details/81027728
循環鏈表
1、循環鏈表:
這個就是在單鏈表的基礎上頭尾相接了,將最後一個結點的指針指向了L->next;這裏我們也不多做贅述,它的大部分操作和單鏈表是相似的。
還有一點要註意的就是判斷一個循環鏈表是否為空條件是看頭結點是否指向其本身。
雙向鏈表
所謂雙向就是說有兩個方向,其實就是在單鏈表的基礎上,多了一個前驅的指針域,用來指向前驅元素,其解決了單鏈表單向性這一缺點(只能順著後繼元素遍歷)
雙向循環鏈表,與循環鏈表類似,帶頭結點的雙向循環空鏈表如下圖(左),非空如下圖(右邊)
那麽入定義一個雙向鏈表呢?
1 typedef struct DulNode
2 {
3 struct DulNode *prior; //前驅指針
4 struct DulNode *next; //後繼指針
5 ElemType e; //數據域
6 }DulNode,*DuLinkList;
插入操作
對比單鏈表學習,復雜並且靈活
插入關鍵代碼:
1 s->prior = p; //把p賦值給s的前驅,s的前驅指向p
2 s->next = p->next; //把p->next賦值給s的後繼
3 p->next->prior = s; //把s賦值給p-next的前驅
4 p->next = s; //把s賦值給p的後繼
刪除操作
刪除操作關鍵代碼:
1 P->prior->next = p->next; //把p->next的值賦給p->prior的後繼
2 p->next->prior = P->prior;//把p=>prior賦值給p->next的前驅
復習回顧:
我們對線性表的兩大結構進行了學習,先是學習了比較容易的順序存儲結構,,指的是用一段地址連續的存儲單元依次存儲線性表中的元素,我們通常使用數組來實現。
後面則是學習了鏈式存儲結構。它具有不收存儲空間的限制,可以比較快捷的插入和刪除的特點被廣為使用。然後還學習了動態鏈表的單鏈表、雙鏈表、循環鏈表,以及最後我們還學習了如何不使用指針處理鏈式結構的數據關系---靜態鏈表。
總的來說,線性表就包含了順序存儲結構和鏈式存儲結構兩大結構。
靜態鏈表、循環鏈表、雙向鏈表(C代碼實現)