數據結構與算法——單鏈表的實現及原理


1. 單鏈表的原理

  鏈表是線性表的鏈式存儲方式,邏輯上相鄰的數據在計算機內的存儲位置不必須相鄰,那么怎么表示邏輯上的相鄰關系呢?可以給每個元素附加一個指針域,指向下一個元素的存儲位置。如圖所示:

  

  從圖中可以看出,每個結點包含兩個域:數據域和指針域,指針域存儲下一個結點的地址,因此指針指向的類型也是結點類型鏈表的核心要素:Ø 每個節點由數據域和指針域組成 Ø 指針域指向下一個節點的內存地址。

  1.1 結構體定義

1  Typedef struct LinkNode 
2  {
3      ElemType data;             //節點中存放數據的類型
4      struct LinkNode* next;     //節點中存放下一節點的指針
5  }LinkList, LinkNode;

 

 

2. 單鏈表初始化

鏈表的節點均單向指向下一個節點,形成一條單向訪問的數據鏈

  

  

 1  //單鏈表的初始化
 2     typedef struct _LinkNode
 3     {
 4         int data;                      //結點的數據域
 5         struct _LinkNode* next;        //結點的指針域 
 6     }LinkNode, LinkList;               //鏈表節點、鏈表
 7 
 8     bool InitList(LinkList*& L)      //構造一個空的單鏈表 L
 9     {
10         L = new LinkNode;            //生成新結點作為頭結點,用頭指針 L 指向頭結點 
11         if(!L)return false;          //生成結點失敗 
12         L->next=NULL;                //頭結點的指針域置空
13         return true;
14     }

 

 

3. 單鏈表增加元素 - 單鏈表前插法

插入節點的要素就是要找到要插入位置的前一個節點,將這個節點的Next賦值給新節點,然后將新節點的地址賦值給前一個節點的Next便可,任意位置插入和前插法均是如此。

   

1  //前插法
2 bool ListInsert_front(LinkList * &L, LinkNode * node)           //參數1 鏈表指針  參數2 要插入的節點元素
3 {
4     if (!L || !node) return false;          //如果列表或節點為空返回 false
5     node->next = L->next;                   //將頭節點指向節點1的地址 賦值給要插入節點的指針域,使要插入的節點先與后部相連
6     L->next = node;                         //將插入節點的地址賦值給頭結點的指針域,使要插入節點與頭結點相連
7 
8     return true;
9 }

 

 

4. 單鏈表增加元素 - 單鏈表尾插法

 

 1  //尾插法
 2  bool ListInsert_back(LinkList*& L, LinkNode* node) 
 3  {
 4      LinkNode* last = NULL;                         //創建空指針,
 5      if (!L || !node) return false;                 //如果列表或節點為空返回 false                        
 6      
 7      last = L;
 8      while (last->next) last = last->next;          //使用 last 找到最后一個節點
 9      
10      node->next = NULL;                             //要插入節點由於在尾部,指針域置為 NULL
11      last->next = node;                             //將要插入節點的地址賦值給之前的尾部節點的指針域,將要插入節點放置到尾部
12      return true;
13  }

 

 

5. 單鏈表增加元素 - 單鏈表任意位置插入

插入節點的要素就是要找到要插入位置的前一個節點,將這個節點的Next賦值給新節點,然后將新節點的地址賦值給前一個節點的Next便可,任意位置插入和前插法均是如此。

 1  //任意位置插法
 2  bool LinkInsert(LinkList*& L, int i, int& e)       //參數1 鏈表指針  參數2 要插入的位置  參數3 要插入的節點元素
 3  {
 4      LinkList* p = L, * s;
 5      int j = 0;
 6 
 7      while (p && j < i - 1)                         //查找第i-1個結點(要插入位置的前一個節點),p指向該結點
 8      {
 9          p = p->next;
10          j++;
11      }
12 
13      if (!p || j > i - 1)                           //驗證數據有效性
14      {
15          return false;
16      }
17 
18      s = new LinkNode;                              //生成新結點 
19      s->data = e;                                   //將新結點的數據域置為 e
20      s->next = p->next;                             //將新結點的指針域指向結點 ai,使要插入的節點先與后部相連
21      p->next = s;                                   //將結點 p 的指針域指向要插入的結點,使要插入節點與頭結點相連
22 
23      return true;
24  }

 

 

6. 單鏈表遍歷

 1  void LinkPrint(LinkList*& L)                   //單鏈表的輸出
 2  {
 3      LinkNode* p; 
 4      p = L->next;
 5 
 6      while (p)                                  //一直循環打印,直到 p->next == NULL 退出循環
 7      {
 8          cout << p->data << "\t";
 9          p = p->next;
10      }
11      cout << endl;
12  }

 

 

7. 單鏈表獲取元素

 1  //單鏈表的取值
 2  //在帶頭結點的單鏈表 L 中查找第 i 個元素,用 e 記錄 L 中第 i 個數據元素的值
 3 bool Link_GetElem(LinkList*& L, int i, int& e)     //參數1 要查找的鏈表指針  參數2 要查找的元素位置    參數3 要記錄存放元素的值
 4 {
 5     int j; 
 6     LinkList* p;
 7     p = L->next;                           //p指向第一個結點
 8     j=1;                                   //j為計數器
 9 
10     while (j < i && p)                     //順鏈域向后掃描,直到p指向第i個元素或p為空
11     {
12         p = p->next;                       //p指向下一個結點
13         j++;                               //計數器j相應加1
14     }
15 
16     if (!p || j > i) 
17     {
18         return false;                      //i 值不合法 i>n 或 i<=0
19     }
20 
21     e = p->data;                           //取第i個結點的數據域
22     return true;
23 }

 

 

8. 單鏈表查找元素

 1 //按值查找
 2 bool Link_FindElem(LinkList* L, int e)  //在帶頭結點的單鏈表L中查找值為e的元素
 3 {
 4     LinkList* p; 
 5     p = L->next;
 6     while (p && p->data != e)           //順鏈域向后掃描,直到p為空或p所指結點的數據域等於e
 7     {
 8         p = p->next;                    //p指向下一個結點
 9     }
10     if (!p)return false;                //查找失敗p為NULL
11 
12     return true;
13 }

 

 

9. 單鏈表刪除

 1 //單鏈表的節點刪除
 2 bool LinkDelete(LinkList*& L, int i)                    //在帶頭結點的單鏈表L中,刪除第i個位置
 3 {
 4     LinkList* p = L, * q;
 5     int j = 0;
 6 
 7     while (p->next && j < i - 1)                        //查找第i-1個結點,p指向該結點
 8     {
 9         p = p->next; 
10         j++;
11     }
12 
13     if (!(p->next) || j > i - 1)  return false;         //當 i > n 或 i < 1 時,刪除位置不合理 
14         
15     q = p->next;            //臨時保存被刪結點的地址以備釋放空間,(刪除的是 P 的下一個節點)
16     p->next = q->next;      //將要刪除的節點的下一個節點地址覆蓋要刪除節點的地址(就是把要刪除節點的下一個節點地址,賦值給被刪除節點的前一個節點的next)
17     delete q;               //釋放被刪除結點的空間
18 
19     return true;
20 }

 

 

10. 單鏈表銷毀

 1 //單鏈表的銷毀
 2 void LinkDestroy(LinkList*& L)
 3 {
 4     //定義臨時節點p指向頭節點
 5     LinkList* p = L;
 6     cout << "銷毀鏈表!" << endl;
 7 
 8     while (p)                                               //遍歷整個鏈表,逐個釋放直到 L->next == NULL
 9     {
10         L = L->next;                                        //L指向下一個節點
11         cout << "刪除元素: " << p->data << endl; 
12         delete p;                                           //刪除當前節點
13         p = L;                                              //p 移向下一個節點
14     }
15 }

 

 11. 完整實現

  1 #include<iostream>
  2 #include<string>
  3 #include<stdlib.h>
  4 
  5 using namespace std;
  6 
  7 typedef struct _LinkNode 
  8 {
  9     int data;                //結點的數據域
 10     struct _LinkNode* next; //結點的指針域
 11 }LinkNode, LinkList;        //LinkList 為指向結構體LNode 的指針類型
 12 
 13 bool InitList(LinkList*& L) 
 14 {
 15     L = new LinkNode;
 16     if (!L) return false;    //生成節點失敗
 17     L->next = NULL;
 18     L->data = -1;
 19     return true; 8979438401111
 20 }
 21 
 22 //前插法
 23 bool ListInsert_front(LinkList*& L, LinkNode* node) 
 24 {
 25     if (!L || !node) return false;
 26     node->next = L->next;
 27     L->next = node;
 28     return true;
 29 }
 30 
 31 //尾插法
 32 bool ListInsert_back(LinkList*& L, LinkNode* node) 
 33 {
 34     LinkNode* last = NULL;
 35     if (!L || !node) return false;
 36     last = L;
 37     while (last->next) last = last->next;
 38     node->next = NULL;
 39     last->next = node;
 40     return true;
 41 }
 42 
 43 //指定位置插入
 44 bool LinkInsert(LinkList*& L, int i, int& e) 
 45 {
 46     if (!L) return false;
 47     int j = 0;
 48     LinkList* p, * s;
 49     p = L;
 50     while (p && j < i - 1)                //查找位置為i-1 的結點,p 指向該結點
 51     {
 52         p = p->next;
 53         j++;
 54     }
 55     if (!p || j > i - 1) 
 56     {
 57         return false;
 58     }
 59     s = new LinkNode;                    //生成新節點
 60     s->data = e;
 61     s->next = p->next;
 62     p->next = s;
 63     return true;
 64 }
 65 
 66 //遍歷
 67 void LinkPrint(LinkList*& L) 
 68 {
 69     LinkNode* p = NULL;
 70     if (!L) {
 71         cout << "鏈表為空." << endl;
 72         return;
 73     }
 74     p = L->next;
 75     while (p) {
 76         cout << p->data << "\t";
 77         p = p->next;
 78     }
 79     cout << endl;
 80 }
 81 
 82 //單鏈表的取值
 83 bool Link_GetElem(LinkList*& L, int i, int& e)            
 84 {
 85     //在帶頭結點的單鏈表L 中查找第i 個元素
 86     //用e 記錄L 中第i 個數據元素的值
 87     int index;
 88     LinkList* p;
 89     if (!L || !L->next) return false;
 90     p = L->next;
 91     index = 1;
 92     while (p && index < i)                                //順鏈表向后掃描,直到p 指向第i 個元素或p 為空
 93     {
 94         p = p->next;                                    //p 指向下一個結點
 95         index++;                                        //計數器index 相應加1
 96     }
 97     if (!p || index > i) {
 98         return false;                                    //i 值不合法,i>n 或i<=0
 99     }
100     e = p->data;
101     return true;
102 }
103 
104 //按值查找
105 bool Link_FindElem(LinkList* L, int e, int& index)
106 {
107     //在帶頭結點的單鏈表L 中查找值為e 的元素
108     LinkList* p;
109     p = L->next;
110     index = 1;
111     if (!L || !L->next) 
112     {
113         index = 0;
114         return false;
115     }
116     while (p && p->data != e) 
117     {
118         p = p->next;
119         index++;
120     }
121     if (!p) 
122     {
123         index = 0;
124         return false;                                    //查無此值
125     }
126     return true;
127 }
128 
129 //單鏈表的刪除
130 bool LinkDelete(LinkList*& L, int i)
131 {
132     LinkList* p, * q;
133     int index = 0;
134     p = L;
135     if (!L || !L->next) 
136     {
137         return false;
138     }
139 
140     while ((p->next) && (index < i - 1)) 
141     {
142         p = p->next;
143         index++;
144     }
145 
146     if (!p->next || (index > i - 1))                    //當 i>n 或 i<1 時,刪89除7位94置38不40合1理111
147     { 
148         return false;
149     }
150     q = p->next;                                        //臨時保存被刪結點的地址以備釋放空間
151     p->next = q->next;                                    //改變刪除結點前驅結點的指針域
152     delete q;                                            //釋放被刪除結點的空間
153     return true;
154 }
155 
156 //單鏈表的銷毀
157 void LinkDestroy(LinkList*& L)
158 {
159     //定義臨時節點p 指向頭節點
160     LinkList* p = L;
161     cout << "銷毀鏈表!" << endl;
162     while (p) {
163         L = L->next;                                    //L 指向下一個節點
164         cout << "刪除元素: " << p->data << endl;
165         delete p;                                        //刪除當前節點
166         p = L;                                            //p 移向下一個節點
167     }
168 }
169 
170 int main(void) 
171 {
172     LinkList* L = NULL;
173     LinkNode* s = NULL;
174 
175     //1. 初始化一個空的鏈表
176     InitList(L);
177 
178     //2. 使用前插法插入數據
179     /*int n;
180     cout<<"前插法創建單鏈表"<<endl;
181     std::cout<<"請輸入元素個數n:";
182     cin>>n;
183     cout<<"\n 請依次輸入n 個元素:" <<endl;
184     while(n>0){
185     s = new LinkNode; //生成新節點s
186     cin>>s->data;
187     ListInsert_front(L, s);
188     n--;
189     }*/
190 
191     //3. 使用尾插法插入數據
192     /*int n;
193     cout<<"尾插法創建單鏈表"<<endl;
194     std::cout<<"請輸入元素個數n:";
195     cin>>n;
196     cout<<"\n 請依次輸入n 個元素:" <<endl;
197     while(n>0){
198     s = new LinkNode; //生成新節點s
199     cin>>s->data;
200     ListInsert_back(L, s);
201     n--;
202     }*/
203 
204     //4. 單鏈表的輸出
205     /*
206     LinkPrint(L);
207     */
208 
209     //5. 任意位置插入元素
210     for (int j = 0; j < 3; j++) {
211         int i, x;
212         cout << "請輸入插入的位置和元素(用空格隔開):";
213         cin >> i;
214         cin >> x;
215         if (LinkInsert(L, i, x)) {
216             cout << "插入成功.\n\n";
217         }
218         else {
219             cout << "插入失敗!\n\n";
220         }
221         LinkPrint(L);
222     }
223 
224     //6. 單鏈表根據位置獲取元素
225     int element = 0;
226     if (Link_GetElem(L, 2, element)) {
227         cout << "獲取第二個元素成功, 值:" << element << endl;
228     }
229     else {
230         cout << "獲取第二個元素失敗!" << endl;
231     }
232 
233     //7. 單鏈表根據值查詢元素所在的位置
234     int index = 0;
235     if (Link_FindElem(L, 10, index)) {
236         cout << "查找元素10 存在,所在位置: " << index << endl;
237     }
238     else {
239         cout << "不存在元素10." << endl;
240     }
241 
242     //8. 單鏈表刪除元素
243     if (LinkDelete(L, 2)) {
244         cout << "刪除第2 個元素成功!" << endl;
245         LinkPrint(L);
246     }
247     else {
248         cout << "刪除第2 個元素失敗!" << endl;
249     }
250 
251     //9. 銷毀單鏈表
252     LinkDestroy(L);
253     system("pause");
254     return 0;
255 }

 

 

 

 

 

======================================================================================================================


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM