線性表的鏈式存儲(C代碼實現)
線性表的鏈式存儲結構
線性表的實現分順序存儲結構和鏈式存儲結構。
上一節我們學學習了線性表的實現分順序存儲結構,並實現解順序存儲的基本操作。
這一節我們來學習線性表鏈式存儲結構,那我們再想象一下我為什麽我們要引入鏈式存儲結構,萬物存在必有其道理
主要還是因為線性存儲結構存在著這樣一個問題:當我們需要插入和刪除元素時,就必須挪動大量與之無關的元素,因為線性存儲結構結點與節點之間的關系是相鄰關系,一個節點挨著一個節點
如為了插入或者刪除一個元素移動大量的元素,這樣就降低了程序運行效率。
當我們引入
顧名思義鏈式存儲結構,數據與數據之間是以鏈式關系來產生連接的的,我們可以腦部一下鎖鏈的樣子,不扯淡了,進入正題--
我們如何定義一個鏈式存儲結構的節點呢?
/*Node表示一個節點*/
typedef struct Node{
int data; //數據域
struct Node* next; //存放下一個節點的指針
}Node;
typedef struct Node* LinkList; /*取了一個別名,定義LinkList = Node*,用於存放節點的指針*/
一個節點包括一個數據域和指針域
我們將這種只帶有一個指針域的線性表稱為單鏈表。
鏈表中第一個結點的存儲位置叫做頭指針。
單鏈表的第一個結點前附設一個結點,稱為頭結點。
註意可以沒有頭節點,但是要是鏈表,就一定存在頭指針。
那麽問題來了,我們如何區分頭節點和頭指針呢?
頭指針:是指向鏈表的指針,如果不存在頭節點,那麽頭指針就會指向鏈表的第一個節點。
頭節點:實際上是不存在的,只不過是為了鏈表的一些操作方便而設置的,頭節點與第一個節點以鏈式關系相連,並且頭節點的數據域沒有意義,指針域存放第一個節點的地址。
單鏈表的插入
s->next =p->next;
p->next=s; //註意前後順序不能調
單鏈表的刪除
q= p->next;
p->next = q->next;
free(q);
單鏈表的建表(頭插法)
顧名思義直接插在第一位,就是頭節點的後面。
1 void CreateListHead(LinkList *L,int n){
2
3 LinkList p;
4 *L = (Node*)malloc(sizeof(Node)); //生成的新節點節點要初始化
5 (*L)->next=NULL; //並指向空
6
7 for(int i=0;i<n;i++){
8 p =(Node*)malloc(sizeof(Node)); //新生成的節點節點要初始化
9 p->data=i;
10 p->next=(*L)->next; //這裏不能指向NULL
11 (*L)->next =p; //兩級指針
12 }
13 }
單鏈表的建表(尾插法)
1 void CreateListTail(LinkList *L ,int n){
2
3 LinkList p,r; //生成節點p,存放Node地址
4 *L = (Node*)malloc(sizeof(Node)); //生成的頭節點節點要初始化
5 (*L)->next=NULL;
6 r = *L; //用於遍歷
7
8 for(int i=0;i<n;i++){
9 p =(Node*)malloc(sizeof(Node)); //新生成的節點節點要初始化
10 p->data=i;
11 r->next=p;
12 r=p; //r指針移動到p上r ,以便
13 }
14 r->next=NULL; //最後一個指向空
15 }
單鏈表的遍歷
1 void TraverseList(LinkList L){
2 LinkList p;
3 p = L->next;
4 while(p){
5 printf("%d ",p->data);
6 p=p->next;
7 }
8 }
清空單鏈表
1 /*清空鏈表*/
2 void ClearList(LinkList L){
3 LinkList p ,q;
4 p = L->next; //指向第一個元素
5 while(p){
6 q=p->next; //q指向了p的下一個
7 free(p); //釋放內存
8 p =q;
9 }
10 L->next =NULL; //頭節點指向空
11 }
其它的一下操作見下面代碼吧
1 #include <stdio.h> 2 #include <stdlib.h> 3 #include "time.h" 4 5 /*Node表示一個節點*/ 6 typedef struct Node{ 7 int data; 8 struct Node* next; 9 }Node; 10 typedef struct Node* LinkList; /*定義LinkList*/ 11 12 /*初始化鏈表*/ 13 int InitList(LinkList *L){ 14 *L=(LinkList)malloc(sizeof(Node)); //這裏的*L就是Node節點的指針對象 15 if(!(*L)) //申請內存失敗 16 return 0; 17 (*L)->next=NULL; 18 return 1; 19 } 20 21 //頭插法,n表示插入的個數 22 void CreateListHead(LinkList *L,int n){ 23 24 LinkList p; 25 *L = (Node*)malloc(sizeof(Node)); //生成的新節點節點要初始化 26 (*L)->next=NULL; //並指向空 27 28 for(int i=0;i<n;i++){ 29 p =(Node*)malloc(sizeof(Node)); //新生成的節點節點要初始化 30 p->data=i; 31 p->next=(*L)->next; //這裏不能指向NULL 32 (*L)->next =p; //兩級指針 33 } 34 } 35 //尾插法,n表示插入的個數 36 void CreateListTail(LinkList *L ,int n){ 37 38 LinkList p,r; //生成節點p,存放Node地址 39 *L = (Node*)malloc(sizeof(Node)); //生成的頭節點節點要初始化 40 (*L)->next=NULL; 41 r = *L; //用於遍歷 42 43 for(int i=0;i<n;i++){ 44 p =(Node*)malloc(sizeof(Node)); //新生成的節點節點要初始化 45 p->data=i; 46 r->next=p; 47 r=p; //r指針移動到p上r ,以便 48 } 49 r->next=NULL; //最後一個指向空 50 } 51 52 /*遍歷鏈表*/ 53 void TraverseList(LinkList L){ 54 LinkList p; 55 p = L->next; 56 while(p){ 57 printf("%d ",p->data); 58 p=p->next; 59 } 60 } 61 62 /*清空鏈表*/ 63 void ClearList(LinkList L){ 64 LinkList p ,q; 65 p = L->next; //指向第一個元素 66 while(p){ 67 q=p->next; //q指向了p的下一個 68 free(p); //釋放內存 69 p =q; 70 } 71 L->next =NULL; //頭節點指向空 72 } 73 74 /*獲取鏈表長度*/ 75 int GetLengthList(LinkList L){ 76 LinkList p; 77 p=L->next; 78 int count=0; //計數器 79 while(p){ 80 count++; 81 p= p ->next; 82 } 83 return count; 84 85 } 86 87 /*刪除元素*/ 88 void DeleteElem(LinkList L,int n){ 89 int count=0; 90 LinkList p ,q; 91 p =L; //註意這裏的p不在指向第一個節點了 92 count =1; 93 while(p->next && count<n){ // 94 p =p->next; 95 ++count; 96 } 97 if(!(p->next) || count>n) 98 printf("沒有找到可刪除的元素"); 99 q= p->next; 100 p->next = q->next; 101 free(q); 102 } 103 104 /*插入元素 n是位置,c是數*/ 105 void InsertElemList(LinkList L,int n,int c){ 106 107 int count=0; 108 LinkList p,s; 109 p =L; //註意這裏的p不在指向第一個節點了 110 count =1; 111 while(p->next && count<n){ // 112 p =p->next; 113 ++count; 114 } 115 s =(Node*)malloc(sizeof(Node)); 116 s->data=c; 117 s->next =p->next; 118 p->next=s; 119 120 } 121 122 /* 初始條件:順序線性表L已存在 */ 123 /* 操作結果:返回L中第1個與e滿足關系的數據元素的位序。 */ 124 /* 若這樣的數據元素不存在,則返回值為0 */ 125 int LocateElem(LinkList L,int e){ 126 127 LinkList p; 128 p =L->next; 129 while(p){ 130 if(p->data == e) 131 return 1; 132 p =p->next; 133 } 134 return 0; 135 } 136 137 /*獲取元素,n表示第幾個,並返回查到的值*/ 138 int GetElem(LinkList L,int n){ 139 140 LinkList p; //生成節點p,存放Node地址 141 p = L->next; //p指向第一個元素 142 int count=1; //計數器 143 144 while(p && count<n){ //查找 145 p = p->next; 146 count++; 147 } 148 if(!p || count>n){ //沒找到 149 return 0; 150 } 151 int ret = p->data; //找到 152 return ret; 153 } 154 155 /*判斷鏈表是否為空*/ 156 int ListElmpty(LinkList L){ 157 158 if(L->next){ 159 return 0; 160 }else{ 161 return 1; 162 } 163 } 164 165 int main(){ 166 LinkList L1; //創建一個節點,用於頭插法 167 LinkList L2; //創建一個節點,用於尾插法 168 169 printf("......頭插法......\n"); 170 InitList(&L1); //初始化 171 CreateListHead(&L1,5); 172 TraverseList(L1); 173 printf("\n"); 174 175 printf("......尾插法......\n"); 176 InitList(&L2); //初始化 177 CreateListTail(&L2,5); 178 TraverseList(L2); 179 printf("\n"); 180 181 //獲取元素的值 182 int getElem= GetElem(L2,3); 183 printf("%d \n",getElem); 184 185 //獲取長度 186 int GetLength=GetLengthList(L2); 187 printf("L1鏈表的長度:%d",GetLength); 188 printf("\n"); 189 190 //刪除L1中2號元素 191 printf("刪除L1中2號元素:"); 192 DeleteElem(L1,2); 193 TraverseList(L1); 194 printf("\n"); 195 196 //在第三個位置插入11 197 printf("在第三個位置插入11元素:"); 198 InsertElemList(L1,3,11); 199 TraverseList(L1); 200 printf("\n"); 201 202 int localFind=LocateElem(L1,11); 203 printf("找到了嗎: %d\nd",localFind); 204 205 //判斷L1是否為空 206 int lstElempty=ListElmpty(L1); 207 printf("L1為空嗎: %d\n",lstElempty); 208 //清空L1 209 ClearList(L1); 210 //在判斷L1是否為空 211 lstElempty=ListElmpty(L1); 212 printf("L1為空嗎: %d\n",lstElempty); 213 214 return 0; 215 }View Code
寫完插入和刪除操作,我們便可以看出,鏈式存儲結構對於插入和刪除的優勢是明顯的,不需要進行大量的元素的移動。
當然單鏈表這麽個優秀,也是存在缺點的
缺點就是其不便於進行查找和修改,每查找或者修改一個元素就要開始從頭開始遍歷 - -這麽坑爹的嗎 ?沒錯 就是這麽坑爹 - -
所以當我們應用的場合不同 ,就用不同的存儲結構。
線性表的鏈式存儲(C代碼實現)