1. 程式人生 > >線性表的鏈式存儲(C代碼實現)

線性表的鏈式存儲(C代碼實現)

移動 為什麽 就會 意義 alt main 放下 emp 指向

線性表的鏈式存儲結構

線性表的實現分順序存儲結構鏈式存儲結構。

上一節我們學學習了線性表的實現分順序存儲結構,並實現解順序存儲的基本操作。

這一節我們來學習線性表鏈式存儲結構,那我們再想象一下我為什麽我們要引入鏈式存儲結構,萬物存在必有其道理

主要還是因為線性存儲結構存在著這樣一個問題:當我們需要插入和刪除元素時,就必須挪動大量與之無關的元素,因為線性存儲結構結點與節點之間的關系是相鄰關系,一個節點挨著一個節點

如為了插入或者刪除一個元素移動大量的元素,這樣就降低了程序運行效率。

當我們引入

顧名思義鏈式存儲結構,數據與數據之間是以鏈式關系來產生連接的的,我們可以腦部一下鎖鏈的樣子,不扯淡了,進入正題--

我們如何定義一個鏈式存儲結構的節點呢?

/*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代碼實現)