單鏈表的操作


       學習數據結構,進行單鏈表操作是很基礎的內容;只要掌握單鏈表,那么循環鏈表、棧和隊列的操作將是水到渠成的事情。單鏈表的難點在於結構體和指針的配合使用,這點掌握熟練,那么單鏈表也不在話下。這篇文章的示例程序是在Ubuntu16.04操作系統環境中進行的。

       我們學習鏈表的目的是什么?也就是說我們學習鏈表是要解決什么樣的問題呢?大家都知道,針對數組,一組數據的數據類型少,產生了結構體,而結構體和數組都有一個共同的特點,在定義的時候就要規定明確的數據個數。所以對於有限個數據的處理,我們使用結構體或數組就夠用了,但是,很多實際問題,我們無法在定義的時候能夠明確具體的數據個數,很多時候會有未知個數的數據需要處理,這時我們就需要使用鏈表來進行操作了。

       下面我們便以代碼為例,簡單說明一下單鏈表的操作。

       首先編寫頭文件,頭文件的名稱為:linklist.h。聲明結構體,聲明各個操作函數。一般的單鏈表操作,是不會在節點中加入編號的,而我個人認為,加入編號方便后續的編程實現,也不容易產生混亂,可以進一步驗證正確與否,盡管這樣做,對於代碼編寫難度略有提高。

 1 /*
 2 *    文件名為:linklist.h
 3 *    文件作用:聲明結構體,聲明各個操作函數,便於主函數的調用
 4 *    文件用途:用於單鏈表的操作使用
 5 */
 6 typedef int datatype;        /*自定義變量datatype 便於閱讀。*/
 7 /*    定義結構體部分*/
 8 typedef struct node{
 9     datatype data;
10     int no;
11     struct node * next;
12 }listnode,*linklist;
13 
14 /*    聲明各個操作函數*/
15 /*    聲明創建空表函數,返回值為空表指針*/
16 linklist list_create(void);
17 /*    聲明頭結點插入節點函數,返回值為操作是否成功,成功:0失敗:-1*/
18 int list_head_insert(linklist H,datatype x);
19 /*    聲明按節點編號插入節點函數,返回值為操作是否成功,成功:0失敗:-1*/
20 int list_pos_insert(linklist H,datatype x,int pos);
21 /*    聲明按數值查找函數,返回值為節點編號,失敗:-1*/
22 int list_number_search(linklist H,datatype x);
23 /*    聲明按節點序號查找函數,返回值為該節點編號下的數值,失敗:-1*/
24 int list_pos_search(linklist H,int pos);
25 /*    聲明刪除指定節點編號的節點函數,返回值為操作是否成功,成功:0失敗:-1*/
26 int list_delete(linklist H,int pos);
27 /*    聲明將鏈表數據全部倒置,返回值為操作是否成功,成功:0失敗:-1*/
28 int list_invode(linklist H);
29 /*    聲明鏈表輸出顯示各個節點數據的函數,返回值為操作是否成功,成功:0失敗:-1*/
30 int list_show(linklist H);

       以上便是頭文件的編寫,下面創建linklist.c文件,用於各個函數功能的實現。由於文件中代碼行數較多,不方便講述,所以下面的講述,不是將代碼整段貼在下面,而是以函數為單位進行了分割,組合起來和源文件也是一模一樣的。

       第一個函數功能是創建空表,函數沒有參數,但是返回值是指向創建空表的指針。首先要創建動態內存,賦值給空表指針,判斷指針是否為空來確定動態內存是否分配成功,若成功,則繼續給空表內的各個數值及指針賦值。最后返回指針,完成空表的創建。

 

 

 

 1 #include <stdio.h>
 2 #include <stdlib.h>
 3 #include "linklist.h"
 4 linklist list_create(void)
 5 {
 6     linklist H = NULL;
 7 /*    申請動態內存,賦值給指針*/
 8     H = (linklist)malloc(sizeof(listnode));
 9 /*    判斷動態內存是否申請成功*/
10     if(H == NULL)
11     {
12         printf("no memory\n");
13         return NULL;
14     }else
15     {
16 /*    給空表中結構體中的各個成員賦值*/
17         H -> data = 0;
18         H -> no = -1;
19         H -> next = NULL;
20 /*    返回空表指針*/
21         return H;
22     }
23 }

       第二個函數功能是,在頭結點之后插入結點元素。函數有兩個參數,第一個參數是:所要插入節點的鏈表指針,第二個參數是:插入節點中結構體成員中數據的數值。返回值為操作是否成功,若成功則返回0,若失敗則返回-1。

       首先要判斷傳參傳入的鏈表指針是否為空,若為空說明是一個失敗的鏈表,不能繼續執行插入操作,因此返回-1,就此程序結束;若不為空則繼續執行插入操作。在執行插入之前先要創建一個節點,節點的創建也需要申請動態內存管理空間,將動態內存申請的節點指針,賦予頭結點的節點指針,頭結點的節點指針賦予動態內存申請的指針,將參數傳入的數據賦值給新創建的節點結構體中的成員,成員編號賦予0,表示頭結點的下一個編號。由於每次調用這個函數,我們不能確定是第幾次給頭結點插入節點,因此節點的編號必然需要刷新,這樣構建循環,在循環中讓插入的節點的下一個節點依次自增1,使編號不會混亂。最后返回0值表明程序順利執行。

 

 

 

 

 

 1 int list_head_insert(linklist H,datatype x)
 2 {
 3     linklist P = NULL;
 4 /*    判斷傳入的鏈表是否為空*/
 5     if(H == NULL)
 6     {
 7         printf("linklist H is NULL\n");
 8         return -1;
 9     }else
10     {
11 /*    創建節點,動態內存*/
12         P = (linklist)malloc(sizeof(listnode));
13         if(P == NULL)
14         {
15             printf("no memory\n");
16             return -1;
17         }else
18         {
19 /*    插入節點操作,直接賦值*/
20             P -> next = H -> next;
21             H -> next = P;
22             P -> data = x;
23             P -> no = H -> no + 1;
24 /*    刷新各個節點的編號*/
25             while(P -> next != NULL)
26             {
27                 P -> next -> no ++;
28                 P = P -> next;
29             }
30             return 0;
31         }
32     }
33 }

       第三個函數的功能是,依據節點編號插入節點。函數有三個參數,第一個參數是:所要插入節點的鏈表指針;第二個參數是:所要插入節點的數據值;第三個參數是:所要插入節點在鏈表中的節點序號。返回值為操作是否成功,若成功則返回0,若失敗則返回-1。

       程序的開始依舊是判斷傳參傳入的鏈表指針是否為空,若不為空,則繼續執行,申請動態內存創建節點,首先要循環找到指定編號的指針的前一個指針(因為是單鏈表,所以沒有反向的連接以進行靈活的賦值),然后新創建的節點指針賦予指定編號的前一個節點指針,指定編號的前一個節點指針賦予新創建的指針,之后便是成員賦值和刷新編號。

 

 

 1 int list_pos_insert(linklist H,datatype x,int pos)
 2 {
 3     linklist P = NULL;
 4     linklist Q = NULL;
 5 /*    判斷傳入的鏈表指針是否為空*/
 6     if(H == NULL)
 7     {
 8         printf("linklist H is NULL\n");
 9         return -1;
10     }else
11     {
12 /*  創建新節點*/
13         P = (linklist)malloc(sizeof(listnode));
14         if(P == NULL)
15         {
16             printf("no memory\n");
17             return -1;
18         }else
19         {
20 /*    找到指定編號的前一個節點*/
21             Q = H;
22             while(Q -> next -> no != pos)
23             {
24                 Q = Q -> next;
25             }
26 /*    插入操作*/
27             P -> next = Q -> next;
28             Q -> next = P;
29 /*    成員賦值*/
30             P -> data = x;
31 /*    刷新節點編號*/
32             P -> no = pos;
33             while(P -> next != NULL)
34             {
35                 P -> next -> no ++;
36                 P = P -> next;
37             }
38             Q = NULL;
39             P = NULL;
40             return 0;
41         }
42     }
43 }

       第四個和第五個函數功能分別是按值查找和按位查找,代碼很簡單,也沒有復雜的刷新編號操作,在此就不再贅述。僅將代碼陳述其下,以供參考。

 1 int list_number_search(linklist H,datatype x)
 2 {
 3     linklist P = NULL;
 4     if(H == NULL)
 5     {
 6         printf("linklist H is NULL\n");
 7         return -1;
 8     }else
 9     {
10         P = H;
11         while(P -> data != x)
12         {
13             P = P -> next;
14         }
15         return P -> no;
16     }
17 }
18 
19 /*按位查找的返回值是指定節點編號的數據值*/
20 int list_pos_search(linklist H,int pos)
21 {
22     linklist P = NULL;
23     if(H == NULL)
24     {
25         printf("linklist H is NULL\n");
26         return -1;
27     }else
28     {
29         P = H;
30         while(P -> no != pos)
31         {
32             P = P -> next;
33         }
34         return P -> data;
35     }
36 }

       第六個函數功能是,刪除指定編號的節點。函數有兩個參數,第一個參數是:要刪除節點的鏈表的指針;第二個參數是:指定刪除的節點編號。返回值是操作是否成功,若成功則返回0,若失敗則返回-1。

       第一步是要找到指定編號的節點前一個節點的指針。第二步指針賦值,第三部釋放刪除節點的動態內存空間,第四步刷新節點編號,第五步返回操作結果。

 

 

 1 int list_delete(linklist H,int pos)
 2 {
 3     linklist P = NULL;
 4     linklist Q = NULL;
 5 /*    判斷傳入鏈表指針是否為空*/
 6     if(H == NULL)
 7     {
 8         printf("linklist H is NULL\n");
 9         return -1;
10     }else
11     {
12 /*    找到指定編號節點的前一個節點的指針*/
13         P = H;
14         while(P -> next -> no != pos)
15         {
16             P = P -> next;
17         }
18 /*    刪除操作*/
19         Q = P -> next;
20         P -> next = Q -> next;
21         free(Q);
22         Q = NULL;
23 /*    刷新節點編號*/
24         while(P -> next != NULL)
25         {
26             P -> next -> no --;
27             P = P -> next;
28         }
29         P = NULL;
30         return 0;
31     }
32 }

       第七個函數功能是,將鏈表中的節點依次倒置。函數只有一個參數,就是傳入待倒置的鏈表指針。返回值是操作是否成功,若成功則返回0,若失敗則返回-1。

       實現倒置的原理很簡單,就是將鏈表一分為二,然后依次從頭結點重新插入,這樣便實現了倒置操作。着重提示的便是節點編號的刷新問題。

 

 

 1 int list_invode(linklist H)
 2 {
 3     linklist P = NULL;
 4     linklist Q = NULL;
 5 /*    判斷傳入鏈表的指針是否為空*/
 6     if(H == NULL)
 7     {
 8         printf("linklist H is NULL\n");
 9         return -1;
10     }else
11     {
12 /*    將鏈表一分為二*/
13         P = H -> next;
14         H -> next = NULL;
15 /*    依次從頭結點插入節點*/
16         while(P -> next != NULL)
17         {
18             Q = P -> next;
19             P -> next = H -> next;
20             H -> next = P;
21             P -> no = 0;
22 /*    刷新結點編號*/
23             while(P -> next != NULL)
24             {
25                 P -> next -> no ++;
26                 P = P -> next;
27             }
28             P = Q;
29         }
30 /*    為最后一個節點補充插入操作*/
31         P -> next = H -> next;
32         H -> next = P;
33         P -> no = 0;
34         while(P -> next != NULL)
35         {
36             P -> next -> no ++;
37             P = P -> next;
38         }
39         return 0;
40     }
41 }

       第八個函數功能是,輸出顯示鏈表數據。函數只有一個參數,待顯示數據的鏈表指針。返回值是操作結果是否成功,若成功則返回0,若失敗則返回-1。

 1 int list_show(linklist H)
 2 {
 3     linklist P = NULL;
 4 /*    判斷傳入鏈表指針是否為空*/
 5     if(H == NULL)
 6     {
 7         printf("linklist H is NULL\n");
 8         return -1;
 9     }else
10     {
11 /*    輸出打印數值*/
12         P = H -> next;
13         while(P != NULL)
14         {
15             printf("H -> data number %d is %d\n",P -> no,P -> data);
16             P = P -> next;
17         }
18         return 0;
19     }
20 }

       以上便是linklist.c的文件內容,下面創建test.c的文件,用於測試函數的功能是否正確、完整。

 1 #include <stdio.h>
 2 #include <stdlib.h>
 3 #include "linklist.h"
 4 int main()
 5 {
 6     linklist H = NULL;
 7 /*    創建空鏈表*/
 8     H = list_create();
 9 /*    從頭結點插入節點*/
10     list_head_insert(H,10);
11     list_head_insert(H,20);
12     list_head_insert(H,30);
13     list_head_insert(H,40);
14     list_head_insert(H,50);
15     list_show(H);
16     printf("insert 2=========================\n");
17 /*    插入操作演示*/
18     list_pos_insert(H,99,2);
19     list_show(H);
20     printf("delete 2=========================\n");
21 /*    刪除操作演示*/
22     list_delete(H,2);
23     list_show(H);
24     printf("invode===========================\n");
25 /*    倒置操作演示*/
26     list_invode(H);
27     list_show(H);
28     return 0;
29 }

       之后便創建Makefile文件,進行編譯。

1 CFLAGS=-c –Wall –g
2 test:linklist.o test.o
3 .PHONY:clean
4 clean:
5     rm *.o test

 


免責聲明!

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



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