數據結構學習筆記

 最近在看國嵌唐老師的數據結構視頻,以爲還不錯,因此就把筆記記錄下來

本節知識點:

1.數據之間的邏輯結構:
   集合結構:數據元素之間沒有特別的關係,僅同屬相同集合
   線性結構:數據元素之間是一對一的關係
   樹形結構:數據元素之間存在一對多的層次關係
   圖形結構:數據元素之間是多對多的關係
2.數據之間的物理結構
    順序存儲結構:將數據存儲在地址連續的存儲單元裏
    鏈式存儲結構:將數據存儲在任意的存儲單元裏,經過保存地址的方式找到相關的數據元素
3.數據結構是相互之間存在一種或多種特定關係的數據元素的集合
4.程序 = 數據結構 + 算法
5.大O表示法:算法效率嚴重依賴於操做數量, 首先關注操做數的最高次項,操做數的估計能夠做爲時間和空間複雜度的估算,在沒有特殊說明的時候, 咱們應該分析複雜度的最壞狀況
6.常見的複雜度類型:
大小關係:
7.線性表是零個或多個數據元素的集合,之間的元素是有順序的,個數是有限的,數據類型必須相同。線性表包含兩種存儲方式,一種是順序表,另外一種鏈表。
8.對於線性表的使用是這樣的:應該是在設計算法的時候,考慮算法中使用的數據,這些數據之間是什麼關係的,若是是符合線性表特質的,就選擇線性表做爲數據結構。
9.順序表與數組的關係:其實順序表就是在數組的基礎上構建的,本質跟數組是同樣的,只是在數組的基礎上增長了length長度,capacity容量等特性,而後補充了一些列,增、刪、改、查的功能。
10. 我以爲鏈表比順序表最大的優點,就在於鏈表的刪除和插入要比順序表簡單的多,並且當線性表長度很大的時候很難開闢出整段的連續空間!!!最重要的是順序表在建立的時候長度就固定了,再也改變不了了,而鏈表則能夠根據狀況動態增長,這一點是順序表不管怎麼樣都不可能實現的!!!
 
順序表的優勢是:無需爲線性表中的邏輯增長額外的空間,能夠快速的經過下標的方式找到表中的合法位置。
11.線性表的經常使用操做:建立線性表、銷燬線性表、清空線性表、將元素插入線性表、將元素從線性表中刪除、獲取線性表中某個位置的元素、獲取線性表的長度

本節代碼:

1.本節的代碼是一個能夠適合各類類型的順序表,之因此可以適合各類類型,是由於它在順序表中保存的是元素的地址(其實就是一個指針數組)。
2.代碼中的描述順序表的結構體中的元素介紹:length是順序表中有元素的個數、capacity是順序表的容量、node是順序表的頭地址(也是這個指針數組的頭地址)、還有一個就是pos,pos是在刪除和插入的時候使用的一個參數,它表明的是插入到順序表位置的下標(數組的下標 是從0開始的 這個很要注意)。順序表中有length個元素 下標是從0到length-1的。 要注意的是 操做順序表不一樣功能函數的pos的容許範圍是不同的。
3.本節代碼對於函數參數的合法性判斷是極其重視的,這個規範是值得學習的。
4.本節代碼中對於順序表的操做函數,凡是外界輸入的,和輸出到外界的,都是void *類型的,這樣就保證了只有在這些操做函數中才能去改變   描述順序表的結構體裏面的值,在其餘文件的函數中接受到的都是void *類型,沒法直接給這個結構體中的值進行改變,這樣的封裝,保證了代碼的安全性。
5.對於本節代碼最值得思考的地方,常見的順序表是typedef一個A類型,而後在順序表中定義一個這個A類型的數組和length順序表元素個數,這個順序表中是好多個A類型的順序集合,佔用空間的大小是sizeof(A)*capacity。而本節的順序表中是好多個unsigned int *地址類型的順序集合,表中只有地址,第一節省了順序表的空間,第二這樣能夠變相的保存不一樣類型的數據,第三它實現了 順序表(即數據結構) 和 咱們打算利用的數據(即元素)的分離。例如:linux內核鏈表(一個雙向循環鏈表)就是一套單獨的鏈表體制,這個鏈表用在不少機制上面,它就是變相的存儲了好多類型的數據,而且實現了鏈表和數據的分離。
因此在main.c中  數據要想保存在這個順序表中  就應該先給這些數據開闢內存    由於順序表中沒有他們呆的地方   順序表中只能保存他們的地址。
如圖:
代碼以下:
Seqlist.c:
[cpp]  view plain copy
  1. /************************************************************************************  
  2. 文件名:Seqlist.c 
  3. 頭文件:Seqlist.h  
  4. 時間: 2013/08/05  
  5. 做者: Hao  
  6. 功能:能夠複用 帶有增 刪 改 查 功能的順序表 
  7. 難點:1.順序表中存放的都是 各類數據的地址 
  8.       2.void *是用來隔離封裝用的 保證順序表結構體只能被特定的函數改變                                                                                                                                  
  9. ************************************************************************************/  
  10. #include <stdio.h>  
  11. #include <malloc.h>  
  12. #include "Seqlist.h"  
  13.   
  14. typedef unsigned int TSeqListNode;//這個順序表中存放的是 各類數據的地址 因此用unsigned int   
  15. typedef struct str_SeqList  
  16. {  
  17.     int length;//順序已用的長度   
  18.     int capacity;//順序表的總容量   
  19.     TSeqListNode* node;//這個指針是用來在順序表中游走讀取數據用的   
  20. }TSeqList;  //定義描述順序表的結構體   
  21.   
  22. /************************************************************************************  
  23. 函數名:   Creat_SeqList 
  24. 函數功能: 建立一個容量爲capacity的順序表  
  25. 參數:     int capacity 建立順序表中成員的個數 即順序表容量 
  26. 返回值:   void* ret 若是返回NULL 說明建立順序表失敗 
  27.                      若是返回ret 說明建立順序表成功  且ret爲描述順序表的結構體  
  28. ************************************************************************************/  
  29. SeqList* Creat_SeqList(int capacity)  
  30. {  
  31.     TSeqList* ret = NULL;  
  32.     /*進入函數 第一點是先判斷傳人蔘數的合法性*/  
  33.     if(capacity >= 0)  
  34.     {  
  35.         /*給順序表開闢空間*/  
  36.         ret=(TSeqList* )malloc(sizeof(TSeqList)+sizeof(TSeqListNode)*capacity);  
  37.         if(NULL!=ret)//空間開闢成功   給描述順序表的結構體 賦值   
  38.         {  
  39.             ret->capacity=capacity;  
  40.             ret->length=0;  
  41.             ret->node=(TSeqListNode* )(ret+1);//把真正順序表的地址賦給 node   
  42.         }  
  43.     }  
  44.     else  
  45.     {  
  46.         ret = NULL;  
  47.     }   
  48.     return (SeqList*)(ret);  
  49. }   
  50.   
  51. /************************************************************************************  
  52. 函數名:   Destroy_SeqList 
  53. 函數功能: 銷燬順序表   free開闢的內存  
  54. 參數:     void* list 描述順序表結構體指針 
  55. 返回值:   void  
  56. ************************************************************************************/  
  57. void  Destroy_SeqList(SeqList* list)  
  58. {  
  59.     free(list);  
  60. }  
  61.   
  62. /************************************************************************************  
  63. 函數名:  Get_Seqlist_Length 
  64. 函數功能:得到順序表 如今的大小 
  65. 函數參數:void* list 描述順序表結構體指針 
  66. 函數返回值:int ret  成功返回length 
  67.                      失敗返回-1  
  68. ************************************************************************************/  
  69. int Get_Seqlist_Length(SeqList* list)  
  70. {  
  71.     int ret;  
  72.     TSeqList *Tlist=(TSeqList* )list;  
  73.     /*函數參數合法性檢測*/  
  74.     if(NULL != Tlist)  
  75.     {  
  76.         ret=Tlist->length;  
  77.     }   
  78.     else  
  79.         ret=-1;  
  80.     return ret;  
  81. }  
  82.   
  83. /************************************************************************************ 
  84. 函數名:  Get_Seqlist_Capacity 
  85. 函數功能:得到順序表 的容量  
  86. 函數參數:void* list 描述順序表結構體指針 
  87. 函數返回值:int ret  成功返回capacity  
  88.                      失敗返回-1  
  89. ************************************************************************************/  
  90. int Get_Seqlist_Capacity(SeqList* list)  
  91. {  
  92.     int ret;  
  93.     TSeqList *Tlist=(TSeqList* )list;  
  94.     /*函數參數合法性檢測*/  
  95.     if(NULL != Tlist)  
  96.     {  
  97.         ret = Tlist->capacity;  
  98.     }   
  99.     else  
  100.         ret=-1;  
  101.     return ret;  
  102. }  
  103.   
  104. /************************************************************************************  
  105. 函數名:  Clean_Seqlist_Length 
  106. 函數功能:清空順序表  其實就是給length=0;  
  107. 函數參數:void* list 描述順序表結構體指針 
  108. 函數返回值:int ret  成功返回0 
  109.                      失敗返回-1  
  110. ************************************************************************************/  
  111. int Clean_Seqlist_Length(SeqList* list)  
  112. {  
  113.     int ret;  
  114.     TSeqList *Tlist=(TSeqList* )list;  
  115.     /*函數參數合法性檢測*/  
  116.     if(NULL != Tlist)  
  117.     {  
  118.         Tlist->length=0;  
  119.         ret=0;  
  120.     }   
  121.     else  
  122.         ret=-1;  
  123.     return ret;  
  124. }  
  125.   
  126. /************************************************************************************ 
  127. 函數名:  Seqlist_Add 
  128. 函數功能:順序表中有length個數據  在下標爲pos的位置上 插入數據node  因此pos是從0開始的 length是從1開始的  
  129. 參數:      SeqList* list描述順序表的結構體地址   SeqListNode* node插入順序表的數據的地址   
  130.            int pos插入順序表的位置   pos的範圍是從0(此時在順序表頭部插入)開始  到length(此時就是在順序尾部插入) 
  131.             總共是length+1個位置  
  132. 返回值 :  返回1 說明插入數據成功  返回0 說明插入數據失敗 
  133. ************************************************************************************/  
  134. int Seqlist_Add(SeqList* list, SeqListNode* node ,int pos)  
  135. {  
  136.     /*參數合法性檢測*/  
  137.     TSeqList *Tlist=(TSeqList* )list;  
  138.     int ret = (NULL != list);  
  139.     int i;  
  140.     ret=ret && (pos >= 0);  
  141.     ret=ret && (Tlist->length+1 <= Tlist->capacity);  //判斷再插入一個數據的時候  length有沒有超過 capacity   
  142.     if(1 == ret)  
  143.     {  
  144.         if(pos >= Tlist->length)//若是插入的位置pos比 length大的話 默認把length+1賦值給pos   
  145.         {  
  146.             pos = Tlist->length;  
  147.         }  
  148.         for(i=Tlist->length;i>pos;i--)  
  149.         {  
  150.             Tlist->node[i]=Tlist->node[i-1];  
  151.         }   
  152.         Tlist->node[i]=(TSeqListNode)node; //把要插入的地址強制類型轉換成 unsigned int*   
  153.         Tlist->length++;  
  154.     }   
  155.     return ret;//返回1 說明插入數據成功  返回0 說明插入數據失敗   
  156. }     
  157.   
  158.    
  159. /************************************************************************************ 
  160. 函數名:   Get_Node 
  161. 函數功能:找到順序表中下標爲pos的值   
  162. 參數:    pos插入順序表的下標   pos的範圍是從0到length-1    
  163.           SeqList* list描述順序表的結構體地址 
  164. 返回值:  void* ret 找到pos爲下標的那個值 
  165.         若是成功返回pos爲下標的那個值   若是失敗  返回NULL 
  166. ************************************************************************************/  
  167.   
  168. SeqListNode* Get_Node(SeqList* list, int pos)  
  169. {  
  170.     TSeqList* Tlist=(TSeqList* )list;  
  171.     SeqListNode* ret=NULL;  
  172.     if( (NULL!=Tlist) && (pos>=0) && (pos<Tlist->length) )  
  173.     {  
  174.         ret=(SeqListNode* )Tlist->node[pos]; //強制類型轉換成void*    
  175.     }  
  176.     return ret;  
  177. }   
  178.   
  179. /************************************************************************************ 
  180. 函數名:   Del_Node 
  181. 函數功能:找到順序表中下標爲pos的值  而且刪除它  
  182. 參數:    刪除pos爲下標的值   pos的範圍是從0到length-1    
  183.           SeqList* list描述順序表的結構體地址 
  184. 返回值:  void* ret  
  185.           若是成功返回pos爲下標的那個值   若是失敗  返回NULL  
  186. ************************************************************************************/  
  187. SeqListNode* Del_Node(SeqList* list, int pos)  
  188. {  
  189.     TSeqList* Tlist=(TSeqList* )list;  
  190.     SeqListNode* ret=NULL;  
  191.     int i;  
  192.     if( (NULL!=Tlist) && (pos>=0) && (pos<Tlist->length) )  
  193.     {  
  194.         ret=(SeqListNode* )Tlist->node[pos];  
  195.         for(i=pos+1; i<Tlist->length; i++)  
  196.         {  
  197.             Tlist->node[i-1]=Tlist->node[i];  
  198.         }  
  199.         Tlist->length--;  
  200.     }  
  201.     return ret;  
  202. }  

Seqlist.h:
[cpp]  view plain copy
  1. #ifndef __Seqlist__  
  2. #define __Seqlist__  
  3.   
  4. typedef void SeqList;  //是用來封裝 使順序表結構體 不被外界改變 只可被Seqlist.c文件中的函數改變  
  5.                        //由於 這些函數 對外的接口 都是void*    
  6. typedef void SeqListNode;//SeqList 是用來表示 順序表的    SeqListNode是用來表示順序表 中變量的   
  7.   
  8. SeqList* Creat_SeqList(int capacity);  
  9. void  Destroy_SeqList(SeqList* list);  
  10. int Get_Seqlist_Length(SeqList* list);  
  11. int Get_Seqlist_Capacity(SeqList* list);  
  12. int Clean_Seqlist_Length(SeqList* list);  
  13. int Seqlist_Add(SeqList* list, SeqListNode* node ,int pos);  
  14. SeqListNode* Get_Node(SeqList* list, int pos);  
  15. SeqListNode* Del_Node(SeqList* list, int pos);   
  16.   
  17. #endif  

main.c:
[cpp]  view plain copy
  1. #include <stdio.h>  
  2. #include <stdlib.h>  
  3. #include "Seqlist.h"  
  4. int main(int argc, char *argv[])  
  5. {  
  6.     SeqList* My_SeqList = NULL;  
  7.     int a = 10;  
  8.     int b = 5;  
  9.     int c = 3;  
  10.     int d = 6;  
  11.     int e = 1;  
  12.     int *p = NULL;  
  13.     int i = 0;  
  14.     My_SeqList = Creat_SeqList(5);  
  15.     if( NULL != My_SeqList )  
  16.     {  
  17.             Seqlist_Add(My_SeqList, &a ,0);  
  18.             Seqlist_Add(My_SeqList, &b ,0);  
  19.             Seqlist_Add(My_SeqList, &c ,0);  
  20.             Seqlist_Add(My_SeqList, &d ,0);  
  21.             Seqlist_Add(My_SeqList, &e ,0);  
  22.               
  23.             for(i=0; i<Get_Seqlist_Length(My_SeqList); i++)  
  24.             {  
  25.                 p=Get_Node(My_SeqList, i);  
  26.                 printf("%d\n",*p);  
  27.             }  
  28.               
  29.             Del_Node(My_SeqList, 3);  
  30.             for(i=0; i<Get_Seqlist_Length(My_SeqList); i++)  
  31.             {  
  32.                 p=Get_Node(My_SeqList, i);  
  33.                 printf("%d\n",*p);  
  34.             }  
  35.               
  36.     }   
  37.     Clean_Seqlist_Length(My_SeqList);  
  38.     Destroy_SeqList(My_SeqList);  
  39.     return 0;  
  40. }  


test_main.c:
[cpp]  view plain copy
  1. #include <stdio.h>  
  2. #include <stdlib.h>  
  3. #include <malloc.h>  
  4. #include "Seqlist.h"  
  5.   
  6. typedef struct student  
  7. {  
  8.     int student_num;  
  9.     char name[30];  
  10.     char sex[20];  
  11.     int age;  
  12. }str;  
  13. int main()  
  14. {  
  15.     str* str1;  
  16.     SeqList* slist=NULL;  
  17.     int i=0;  
  18.     int age=0;  
  19.     slist=Creat_SeqList(50);  
  20.     if(NULL == slist)  
  21.     {  
  22.         printf("malloc error!!!\n");  
  23.         return -1;  
  24.     }  
  25.     for(i=0; i<3; i++)  
  26.     {  
  27.         put_student(slist, str1);  
  28.     }  
  29.       
  30.     printf("輸入你要刪除的年齡:\n");  
  31.     scanf("%d",&age);  
  32.     printf("\n");  
  33.     find_student(slist, str1, age);  
  34.     get_student(slist, str1);  
  35.       
  36.     destroy_student(slist, str1);  
  37.     Clean_Seqlist_Length(slist);  
  38.     Destroy_SeqList(slist);  
  39.     return 0;  
  40. }  
  41.   
  42. int put_student(SeqList* slist, str* str1)  
  43. {   
  44.     int num;  
  45.     int ret=(NULL != str1);  
  46.     if(1 == ret)  
  47.     {  
  48.         ret=ret && Seqlist_Add(slist, (str* )malloc(sizeof(str)*1) ,50);  
  49.         num = Get_Seqlist_Length(slist);  
  50.         str1 = (str* )Get_Node(slist, num-1);  
  51.         printf("請輸入學生學號:\n");   
  52.         scanf("%d",&str1->student_num);  
  53.         printf("請輸入學生姓名:\n");  
  54.         scanf("%s",str1->name);  
  55.         printf("請輸入學生性別:\n");  
  56.         scanf("%s",str1->sex);  
  57.         printf("請輸入學生年齡:\n");  
  58.         scanf("%d",&str1->age);  
  59.         printf("\n");   
  60.     }         
  61.     else  
  62.     {  
  63.         ret = 0;  
  64.     }  
  65.     return ret;  
  66. }  
  67.   
  68. int get_student(SeqList* slist, str* str1)  
  69. {  
  70.     int ret=(NULL != str1);  
  71.     int i=0;  
  72.     if(1 == ret)  
  73.     {  
  74.         for(i=0; i<Get_Seqlist_Length(slist); i++)  
  75.         {  
  76.             str1 = (str*)Get_Node(slist, i);  
  77.             printf("學生學號:%d\n",str1->student_num);  
  78.           
  79.             printf("學生姓名:%s\n",str1->name);  
  80.               
  81.             printf("學生性別:%s\n",str1->sex);  
  82.               
  83.             printf("學生年齡:%d\n",str1->age);  
  84.         }  
  85.     }         
  86.     else  
  87.     {  
  88.         ret = 0;  
  89.     }  
  90.     return ret;  
  91. }  
  92.   
  93. int destroy_student(SeqList* slist, str* str1)  
  94. {  
  95.     int ret=(NULL != str1);  
  96.     int i=0;  
  97.     if(1 == ret)  
  98.     {  
  99.         for(i=0; i<Get_Seqlist_Length(slist); i++)  
  100.         {  
  101.             str1 = (str*)Get_Node(slist, i);  
  102.             free(str1);  
  103.         }  
  104.     }         
  105.     else  
  106.     {  
  107.         ret = 0;  
  108.     }  
  109.     return ret;  
  110. }  
  111.   
  112. int find_student(SeqList* slist, str* str1, int age)  
  113. {  
  114.     int ret=(NULL != str1);  
  115.     int i=0;  
  116.     int num=0;  
  117.     if(1 == ret)  
  118.     {  
  119.         num=Get_Seqlist_Length(slist);  
  120.         for(i=0; i<num; i++)  
  121.         {  
  122.             str1 = (str*)Get_Node(slist, i);  
  123.             if(str1->age == age)  
  124.             {  
  125.                 Del_Node(slist, i);  
  126.                 num=Get_Seqlist_Length(slist);  
  127.                 i--;  
  128.             }  
  129.         }  
  130.     }         
  131.     else  
  132.     {  
  133.         ret = 0;  
  134.     }  
  135.     return ret;  
  136. }  

test_main.c:
[cpp]  view plain copy
  1. #include <stdio.h>  
  2. #include <stdlib.h>  
  3. #include <malloc.h>  
  4. #include "Seqlist.h"  
  5.   
  6. typedef struct student  
  7. {  
  8.     int student_num;  
  9.     char name[30];  
  10.     char sex[20];  
  11.     int age;  
  12. }str;  
  13. int main()  
  14. {  
  15.     str* str1;  
  16.     SeqList* slist=NULL;  
  17.     int i=0;  
  18.     int age=0;  
  19.     slist=Creat_SeqList(50);  
  20.     if(NULL == slist)  
  21.     {  
  22.         printf("malloc error!!!\n");  
  23.         return -1;  
  24.     }  
  25.     for(i=0; i<3; i++)  
  26.     {  
  27.         put_student(slist, str1);  
  28.     }  
  29.       
  30.     printf("輸入你要刪除的年齡:\n");  
  31.     scanf("%d",&age);  
  32.     printf("\n");  
  33.     find_student(slist, str1, age);  
  34.     get_student(slist, str1);  
  35.       
  36.     destroy_student(slist, str1);  
  37.     Clean_Seqlist_Length(slist);  
  38.     Destroy_SeqList(slist);  
  39.     return 0;  
  40. }  
  41.   
  42. int put_student(SeqList* slist, str* str1)  
  43. {   
  44.     int num;  
  45.     int ret=(NULL != str1);  
  46.     if(1 == ret)  
  47.     {  
  48.         ret=ret && Seqlist_Add(slist, (str* )malloc(sizeof(str)*1) ,50);  
  49.         num = Get_Seqlist_Length(slist);  
  50.         str1 = (str* )Get_Node(slist, num-1);  
  51.         printf("請輸入學生學號:\n");   
  52.         scanf("%d",&str1->student_num);  
  53.         printf("請輸入學生姓名:\n");  
  54.         scanf("%s",str1->name);  
  55.         printf("請輸入學生性別:\n");  
  56.         scanf("%s",str1->sex);  
  57.         printf("請輸入學生年齡:\n");  
  58.         scanf("%d",&str1->age);  
  59.         printf("\n");   
  60.     }         
  61.     else  
  62.     {  
  63.         ret = 0;  
  64.     }  
  65.     return ret;  
  66. }  
  67.   
  68. int get_student(SeqList* slist, str* str1)  
  69. {  
  70.     int ret=(NULL != str1);  
  71.     int i=0;  
  72.     if(1 == ret)  
  73.     {  
  74.         for(i=0; i<Get_Seqlist_Length(slist); i++)  
  75.         {  
  76.             str1 = (str*)Get_Node(slist, i);  
  77.             printf("學生學號:%d\n",str1->student_num);  
  78.           
  79.             printf("學生姓名:%s\n",str1->name);  
  80.               
  81.             printf("學生性別:%s\n",str1->sex);  
  82.               
  83.             printf("學生年齡:%d\n",str1->age);  
  84.         }  
  85.     }         
  86.     else  
  87.     {  
  88.         ret = 0;  
  89.     }  
  90.     return ret;  
  91. }  
  92.   
  93. int destroy_student(SeqList* slist, str* str1)  
  94. {  
  95.     int ret=(NULL != str1);  
  96.     int i=0;  
  97.     if(1 == ret)  
  98.     {  
  99.         for(i=0; i<Get_Seqlist_Length(slist); i++)  
  100.         {  
  101.             str1 = (str*)Get_Node(slist, i);  
  102.             free(str1);  
  103.         }  
  104.     }         
  105.     else  
  106.     {  
  107.         ret = 0;  
  108.     }  
  109.     return ret;  
  110. }  
  111.   
  112. int find_student(SeqList* slist, str* str1, int age)  
  113. {  
  114.     int ret=(NULL != str1);  
  115.     int i=0;  
  116.     int num=0;  
  117.     if(1 == ret)  
  118.     {  
  119.         num=Get_Seqlist_Length(slist);  
  120.         for(i=0; i<num; i++)  
  121.         {  
  122.             str1 = (str*)Get_Node(slist, i);  
  123.             if(str1->age == age)  
  124.             {  
  125.                 Del_Node(slist, i);  
  126.                 num=Get_Seqlist_Length(slist);  
  127.                 i--;  
  128.             }  
  129.         }  
  130.     }         
  131.     else  
  132.     {  
  133.         ret = 0;  
  134.     }  
  135.     return ret;  
  136. }  

線性表有兩種:一種是順序存儲的叫順序表,上節已經說過了,另外一種是鏈式存儲的叫鏈表,本節說的是單鏈表,即單向鏈表(每一個節點中只包含一個指針域)。node

本節知識點:

1.鏈表的好處:對於動態鏈表,能夠對未知數據量的數據進行存儲。插入和刪除比順序表方便的多,不用大量移動。
   鏈表的缺點:除了數據信息,還需對額外的鏈表信息進行分配內存,佔用了額外的空間。訪問指定數據的元素須要順序訪問以前的元素。
2.鏈表的基本概念:
   鏈表頭(表頭節點):鏈表中的第一個節點,包含指向第一個數據元素的指針以及鏈表自身的一些信息(即鏈表長度length)
   數據節點:鏈表中的表明數據元素的節點,包含指向下一個數據元素的指針和數據元素的信息
   尾節點:鏈表中的最後一個數據節點,其下一元素指針爲空,表示無後繼
3. 對於本節的可複用單鏈表的設計想法是這樣的:
   a. 可複用的順序表中保存的是各個數據的地址,因此我最初想到的是在鏈表元素中也保存各個數據的地址:
                                     
     使用這樣的結構,add是鏈表中保存的數據,其實就是想複用保存的各類類型的地址,add是一個unsigned int型,用來保存各類數據類型的地址,next是鏈表結構,用來指向鏈表元素的下一個鏈表元素的。
     b.可是這樣的結構有一個問題,就是從使用的總空間(鏈表結構的空間+add中保存數據的空間)角度來看,add就是一個浪費空間的變量。由於在add中保存地址,爲何不強制類型成next的類型(此時next應該是鏈表第一個結構的類型),直接使用這個地址把各類你想要存儲的結構賦值給next,這樣存儲的各個結構就變成了,如圖。
     
    c.可是把全部的類型都轉換成鏈表第一個元素的指針類型 再賦值給next 顯得程序很不規整,因此最好直接給鏈表一個結構,把這些結構類型都統一強制類型轉換成這個鏈表的類型,以下:
    
[cpp]  view plain copy
  1. typedef struct Str_LinkList LinkListNode;  //這個結構體是鏈表的真身   
  2. struct Str_LinkList   //每個鏈表元素的結構都會包含這個結構  由於當給鏈表元素強制類型   
  3. {                     //轉換成(LinkListNode* )的時候  其實就是要開始對每一個元素中的 LinkListNode進行賦值了   
  4.     LinkListNode* next;  
  5. };  
     把什麼鏈表頭啊,鏈表元素啊,想要鏈接進入這個鏈表的各類結構都強制類型成 LinkListNode*   可是要保證每一個結構中都有LinkListNode* next 成員。
     d.最後一點,就有點接受不了,也是爲了代碼的整潔,提升可讀性,使鏈表結構都規整到LinkListNode這個結構中去,便於對鏈表進行管理,好比說雙向鏈表的前驅和後繼。把每一個類型中的LinkListNode* next 成員  變成LinkListNode node。 這裏面有一個很好的c語言技巧,就是這個LinkListNode node必需要放在每一個結構中(如 str)的第一個元素位置,即node的地址就是結構體str的地址,由於只有這樣了,在把str強制類型轉換成 n=(LinkListNode* )str的時候,訪問n->next纔是訪問str.node->next的值,由於二者地址相同,切記必定要放到第一個元素的位置!!!
4.對於鏈表這個數據結構,必定要注意一個問題,也是這節我犯的一個很難發現的錯誤:
   就是已經在鏈表中的元素,千萬不要再一次往鏈表中進行插入,由於這樣會致使從它插入的地方開始鏈表的後繼就開始混亂了,把整個鏈表徹底弄亂,出現你想不到的問題。

本節代碼:

本節實現的是一個能夠複用的單鏈表:
LinkList.c:
[cpp]  view plain copy
  1. /******************************************************************************************************* 
  2. 文件名:LinkList.c 
  3. 頭文件:LinkList.h  
  4. 時間: 2013/08/07 
  5. 做者: Hao 
  6. 功能:  能夠複用 帶有增 刪 改 查 功能的單鏈表 
  7. 難道: 1.typedef struct Str_LinkList LinkListNode;  //這個結構體是鏈表的真身  
  8.         struct Str_LinkList   //每個鏈表元素的結構都會包含這個結構  由於當給鏈表元素強制類型  
  9.         {                     //轉換成(LinkListNode* )的時候  其實就是要開始對每一個元素中的 LinkListNode進行賦值了  
  10.             LinkListNode* next; 
  11.         };  
  12.         這個鏈表結構在鏈表元素中起到的做用 是本節的難點  
  13.         2.切記一個問題  就是已是鏈表中元素的 千萬不要再往鏈表中添加了 不然鏈表必定出現無窮的錯誤  
  14.         3.對於pos值的問題  add、get、del三個函數中 的鏈表都是 從1開始的到length  0是鏈表頭  
  15.                           在add函數中pos爲0的時候是和pos爲1的狀況是同樣的  都是頭插法  0~~~~~無窮大  
  16.                           在get函數中pos爲0的時候是得到鏈表頭 地址      0~~~~~length  
  17.                           在del函數中pos爲0的時候是無效的 del失敗       1~~~~~length  
  18. *******************************************************************************************************/  
  19. #include <stdio.h>  
  20. #include <stdlib.h>  
  21. #include <malloc.h>  
  22. #include "LinkList.h"  
  23.   
  24. typedef struct str_list_head  //這個是鏈表頭 其實也能夠看成一個沒有前驅的 鏈表元素 元素的內容是鏈表長度   
  25. {  
  26.     //LinkListNode* next;  
  27.     LinkListNode head; //這個參數要特別重視 每個鏈表元素結構的第一個參數必定是 LinkListNode  
  28.                        //由於在尋找鏈表元素後繼的時候 其實就是將鏈表元素強制類型轉換成 LinkListNode*  而後給next進行賦值 其實就是給 LinkListNode變量賦值   
  29.     int length; //鏈表長度   
  30. }list_head;  
  31.   
  32. /******************************************************************************************************* 
  33. 函數名: Creat_LinkListHead 
  34. 函數功能:建立一個鏈表的鏈表頭 並給鏈表頭分配空間 
  35. 參數: void 
  36. 返回值:ret 成功返回鏈表頭地址  失敗返回NULL  
  37. *******************************************************************************************************/  
  38. LinkList* Creat_LinkListHead(void)  
  39. {  
  40.     list_head* ret = NULL;  
  41.     ret = (list_head* )malloc( sizeof(list_head)*1 );  
  42.     if(NULL != ret) //malloc分配成功   
  43.     {  
  44.         ret -> length = 0;  
  45.         //ret -> next = NULL;  
  46.         ret -> head.next = NULL;  
  47.     }  
  48.     return (LinkList* )ret;   
  49. }  
  50.   
  51. /******************************************************************************************************* 
  52. 函數名:Destroy_LinkListHead 
  53. 函數功能:釋放一個鏈表頭指針  
  54. 參數:LinkList* head 鏈表頭指針  
  55. 返回值: ret 釋放成功返回1  釋放失敗返回0  
  56. *******************************************************************************************************/  
  57. int Destroy_LinkListHead(LinkList* head)  
  58. {  
  59.     int ret = 0;   
  60.     list_head* lhead = (list_head* )head;  
  61.     if( NULL != lhead )  
  62.     {  
  63.         free(lhead);  
  64.         ret = 1;  
  65.     }  
  66.     return ret;  
  67. }  
  68.   
  69. /******************************************************************************************************* 
  70. 函數名:Get_Length 
  71. 函數功能:得到鏈表的長度  
  72. 參數: LinkList* head 鏈表頭指針  
  73. 返回值: ret 成功返回鏈表長度  失敗返回0  
  74. *******************************************************************************************************/  
  75. int Get_Length(LinkList* head)   
  76. {  
  77.     int ret = 0;  
  78.     list_head* lhead = (list_head* )head;  
  79.     if( NULL != lhead )  
  80.     {  
  81.         ret = lhead -> length;  
  82.     }     
  83.     return ret;  
  84. }  
  85.   
  86. /******************************************************************************************************* 
  87. 函數名:Clean_LinkListHead 
  88. 函數功能:   清空鏈表  
  89. 參數: LinkList* head 鏈表頭指針  
  90. 返回值:ret 成功返回1 失敗返回0  
  91. *******************************************************************************************************/  
  92. int Clean_LinkListHead(LinkList* head)   
  93. {  
  94.     int ret = 0;  
  95.     list_head* lhead = (list_head* )head;  
  96.     if( NULL != lhead )  
  97.     {  
  98.         lhead -> length = 0;  
  99.         //lhead -> next = NULL;  
  100.         lhead -> head.next = NULL;  
  101.         ret = 1;  
  102.     }     
  103.     return ret;  
  104. }  
  105.   
  106. /******************************************************************************************************* 
  107. 函數名:Add_LinkList 
  108. 函數功能:往鏈表裏面添加一個鏈表元素 若是pos的值是0(就是鏈表頭)和1(鏈表的第一元素 鏈表元素個數是從1開始算的)都是頭插法 
  109.           pos的值大於鏈表長度是尾插法  這裏面pos值得注意的是 i=1 pos爲a的時候 是把鏈表元素插入第a個元素的位置  
  110.           當i=0 pos爲a的時候 是把鏈表元素插入 第a個元素位置的後面    切忌:這裏面0位置是鏈表頭指針 從1開始是鏈表元素  
  111. 參數:   LinkList* head鏈表頭指針    LinkListNode* Node插入元素的指針(被強制類型轉化成LinkListNode*)  int pos 插入位置  
  112.          pos的有效值範圍是 從0到無窮大   
  113. 返回值: ret 插入成功返回1  插入失敗返回0  
  114. *******************************************************************************************************/  
  115. int Add_LinkList(LinkList* head, LinkListNode* Node, int pos)  
  116. {  
  117.     int ret = 0;  
  118.     int i = 0;  
  119.     list_head* lhead = (list_head* )head;  
  120.     LinkListNode* node = (LinkListNode* )head;  
  121.     ret=( NULL != node) && ( NULL != Node) && (pos >= 0);  
  122.     if(1 == ret)  
  123.     {  
  124.         for(i=1; ( (i<pos) && (node->next != NULL) ); i++)  
  125.         {  
  126.             node = node->next;  
  127.         }  
  128.         Node -> next = node -> next;  
  129.         node -> next = Node;  
  130.           
  131.         lhead -> length++;   
  132.     }  
  133.     return ret;  
  134. }  
  135.   
  136. /******************************************************************************************************* 
  137. 函數名:Get_LinkListNode 
  138. 函數功能:得到鏈表中第pos個元素位置的鏈表元素 鏈表是從1開始的  0是鏈表頭   pos爲0的時候表示get鏈表頭  
  139. 參數: LinkList* head鏈表頭指針    int pos得到鏈表元素的位置  pos的有效取值範圍是 1 到  length  0是鏈表頭  
  140. 返回值: LinkListNode*類型 第pos個鏈表元素的地址  
  141. *******************************************************************************************************/  
  142. LinkListNode* Get_LinkListNode(LinkList* head, int pos)  
  143. {  
  144.     int ret = 0;  
  145.     int i = 0;  
  146.     list_head* lhead = (list_head* )head;  
  147.     ret=( NULL != lhead) && (pos >= 0) && (pos <= lhead->length);  
  148.     if(1 == ret)  
  149.     {  
  150.         LinkListNode* node = (LinkListNode* )head;  
  151.         for(i=0; i<pos; i++) //執行 pos次   獲得的是第pos位置的node   
  152.         {  
  153.             node = node->next;  
  154.         }     
  155.         return (LinkListNode*)node;  
  156.     }  
  157.     return NULL;  
  158. }  
  159.   
  160. /******************************************************************************************************* 
  161. 函數名:Del_LinkListNode 
  162. 函數功能:刪除鏈表中第pos位置的鏈表元素  
  163. 參數: LinkList* head鏈表頭指針    int pos刪除鏈表元素的位置  pos是刪除的鏈表元素的位置 跟get和add中的 
  164.        pos是配套的  有效取值範圍依然是 1到 length  在這個函數裏面因爲不能刪除鏈表頭 因此pos爲0的時候無效  
  165. 返回值: LinkListNode* ret這個返回值很重要 由於這個刪除僅僅是把鏈表元素踢出了鏈表 並無free開闢的內存 
  166.          應該經過這個返回的地址free  釋放內存 
  167.          刪除成功返回 刪除鏈表元素的地址   刪除失敗返回 NULL  
  168. *******************************************************************************************************/  
  169. LinkListNode* Del_LinkListNode(LinkList* head, int pos)  
  170. {  
  171.     LinkListNode* ret = NULL;  
  172.     int i = 0;  
  173.     list_head* lhead = (list_head* )head;  
  174.     if(( NULL != lhead) && (pos > 0) && (pos <= lhead->length))  
  175.     {  
  176.         LinkListNode* node = (LinkListNode* )head;  
  177.         for(i=1; i<pos; i++)//執行 pos次   獲得的是第pos位置的node  這個方法行不通   
  178.         {                   //由於要想刪除第pos位置的node 應該先找到它上一個鏈表元素   
  179.             node = node->next; //因此這裏面i=1 比get函數少執行了一次  獲得第pos-1位置的node   
  180.         }  
  181.         ret = node->next;  
  182.         node->next = ret->next;     
  183.           
  184.         lhead->length--;  
  185.     }  
  186.     return (LinkListNode*)ret;  
  187. }  

LinkList.h:
[cpp]  view plain copy
  1. #ifndef __LinkList_H__  
  2. #define __LinkList_H__  
  3.   
  4. typedef void LinkList;  //這個是爲了 封裝方便   
  5. typedef struct Str_LinkList LinkListNode;  //這個結構體是鏈表的真身   
  6. struct Str_LinkList   //每個鏈表元素的結構都會包含這個結構  由於當給鏈表元素強制類型   
  7. {                     //轉換成(LinkListNode* )的時候  其實就是要開始對每一個元素中的 LinkListNode進行賦值了   
  8.     LinkListNode* next;  
  9. };  
  10.   
  11. LinkList* Creat_LinkListHead(void);  
  12.   
  13. int Destroy_LinkListHead(LinkList* head);  
  14.   
  15. int Get_Length(LinkList* head);  
  16.   
  17. int Clean_LinkListHead(LinkList* head);  
  18.   
  19. int Add_LinkList(LinkList* head, LinkListNode* Node, int pos);  
  20.   
  21. LinkListNode* Get_LinkListNode(LinkList* head, int pos);  
  22.   
  23. LinkListNode* Del_LinkListNode(LinkList* head, int pos);   
  24.    
  25. #endif  

main.c:
[cpp]  view plain copy
  1. #include <stdio.h>  
  2. #include <stdlib.h>  
  3. #include <malloc.h>  
  4. #include <string.h>  
  5. #include "LinkList.h"  
  6.   
  7. typedef struct student  
  8. {  
  9.     //LinkListNode* next;  
  10.     LinkListNode node;  
  11.     int num;  
  12.     char name[30];  
  13. }str;  
  14. int main(int argc, char *argv[])   
  15. {  
  16.     str str1,str2,str3,str4,str5,str6,*strp;  
  17.     int i=0;  
  18.     LinkList* list_head;  
  19.     list_head = Creat_LinkListHead();  
  20.       
  21.     str1.num = 1;  
  22.     strcpy(str1.name,"haohao");  
  23.       
  24.     str2.num = 2;  
  25.     strcpy(str2.name,"ququ");  
  26.       
  27.     str3.num = 3;  
  28.     strcpy(str3.name,"popo");  
  29.       
  30.     str4.num = 4;  
  31.     strcpy(str4.name,"wowo");  
  32.       
  33.     str5.num = 5;  
  34.     strcpy(str5.name,"tiantian");  
  35.   
  36.     str6.num = 6;  
  37.     strcpy(str6.name,"cheche");  
  38.       
  39.     Add_LinkList(list_head, (LinkListNode*)&str1, 0);  
  40.     Add_LinkList(list_head, (LinkListNode*)&str2, 0);  
  41.     Add_LinkList(list_head, (LinkListNode*)&str3, 0);  
  42.     Add_LinkList(list_head, (LinkListNode*)&str4, 0);  
  43.     Add_LinkList(list_head, (LinkListNode*)&str5, 0);  
  44.     strp = (str*)Del_LinkListNode(list_head, 5);  
  45.     printf("%d\n",strp->num);  
  46.     printf("%s\n",strp->name);  
  47.     printf("\n");  
  48.     for(i=1; i<= Get_Length(list_head); i++)  
  49.     {  
  50.         strp = (str*)Get_LinkListNode(list_head, i);  
  51.         printf("%d\n",strp->num);  
  52.         printf("%s\n",strp->name);  
  53.     }   
  54.     printf("\n");  
  55.     Add_LinkList(list_head, (LinkListNode*)&str6, 3);     
  56.     for(i=1; i<= Get_Length(list_head); i++)  
  57.     {  
  58.         strp = (str*)Get_LinkListNode(list_head, i);  
  59.         printf("%d\n",strp->num);  
  60.         printf("%s\n",strp->name);  
  61.     }   
  62.           
  63.       
  64.     Clean_LinkListHead(list_head);  
  65.     Destroy_LinkListHead(list_head);  
  66.     return 0;  
  67. }  

課後練習:

1.對於上節順序表中的unsigned  int型保存數據地址,可不可用void*   
這個問題已經獲得了唐老師的解答,其實使用void* 最好了,由於使用unsigned int  僅僅可以在32位機上面運行成功,在64位機上運行就會出錯的!!!

2.對於有頭鏈表和無頭鏈表的區別
所謂有頭鏈表,就是有頭結點的鏈表,頭結點是一個鏈表元素,但不存放數據。無頭鏈表就是沒有頭結點的鏈表。 相比之下有頭鏈表比無頭鏈表,方便不少,優勢也不少。
無頭鏈表就是在譚浩強老師的c語言書中的那個鏈表。就是沒有頭結點,只有一個指針指向鏈表第一個元素。這個指針被叫作鏈表頭。      我的建議使用有頭鏈表!!!
3.對順序表和單鏈表添加一個反轉操做
   a.對於順序表 其實就是在不斷的交換
   b.對於鏈表  我以爲仍是使用雙向鏈表吧,解決這個問題就方便了~~~




問題描述:假設有一個沒有頭指針的單鏈表。一個指針指向此單鏈表中間的一個節點(不是第一個,也不是最後一個節點),請將該節點從單鏈表中刪除。


通常鏈表的刪除須要順着頭結點向下找到當前待刪節點的前驅節點,而後讓前驅節點指向後驅節點就好了。這裏,沒有頭結點,就沒辦法找到前驅結點。但咱們能夠採用「狸貓換太子」的作法。咱們把當前結點「當作」是前驅結點,把後續節點當作待刪結點刪除(刪除以前,記下後續結點的值),只須要讓當前結點指向後驅結點的後驅結點。最後把後續結點值賦給當前結點的值。linux

[cpp]  view plain copy
  1. #include<stdio.h>  
  2. #include<stdlib.h>  
  3. #include<assert.h>  
  4.   
  5. typedef struct node{  
  6.     int data;  
  7.     node *next;  
  8. }Node;  
  9.   
  10. void printlist(Node *head_ptr);  
  11. void delete_random_node(Node *current);  
  12.   
  13. int main(){  
  14.     Node n1, n2, n3;  
  15.     n1.data = 10;  
  16.     n1.next = &n2;  
  17.     n2.data = 20;  
  18.     n2.next = &n3;  
  19.     n3.data = 30;  
  20.     n3.next = NULL;  
  21.     printf("Before deleting\n");  
  22.     printlist(&n1);  
  23.     delete_random_node(&n2);  
  24.     printf("\nAfter deleting\n");  
  25.     printlist(&n1);  
  26.     return 0;  
  27. }  
  28.   
  29. void printlist(Node *head_ptr){    
  30.     Node *ptr = head_ptr;   
  31.     while(ptr != NULL){    
  32.         printf("%d    ", ptr->data);    
  33.         ptr = ptr->next;    
  34.     }    
  35.     printf("\n");    
  36. }    
  37.   
  38. void delete_random_node(Node *current){  
  39.     assert(current != NULL);  
  40.     Node *next = current->next;  
  41.     if(next != NULL){  
  42.         current->next = next->next;  
  43.         current->data = next->data;  
  44.     }  
  45. }  

擴展問題


編寫一個函數,給定一個鏈表的頭指針,要求只遍歷一次,將單鏈表中的元素順序反轉過來。

[cpp]  view plain copy
  1. #include<stdio.h>  
  2. #include<stdlib.h>  
  3.   
  4. typedef struct node{  
  5.     int data;  
  6.     node *next;  
  7. }Node;  
  8.   
  9. Node *reverse_linklist(Node *head);  
  10. void printlist(Node *ptr);  
  11.   
  12. int main(){  
  13.     Node n1, n2, n3;  
  14.     Node *head;  
  15.     head = (Node *)malloc(sizeof(Node));  
  16.     head->next = &n1;  
  17.     n1.data = 10;  
  18.     n1.next = &n2;  
  19.     n2.data = 20;  
  20.     n2.next = &n3;  
  21.     n3.data = 30;  
  22.     n3.next = NULL;  
  23.   
  24.     printf("Before reversing\n");  
  25.     printlist(head);  
  26.     printf("\nAftering reversing\n");  
  27.     printlist(reverse_linklist(head));  
  28.     return 0;  
  29. }  
  30.   
  31. void printlist(Node *head_ptr){  
  32.     Node *ptr = head_ptr->next;  
  33.     while(ptr != NULL){  
  34.         printf("%d    ", ptr->data);  
  35.         ptr = ptr->next;  
  36.     }  
  37.     printf("\n");  
  38. }  
  39.   
  40. Node *reverse_linklist(Node *head){  
  41.     Node *p = head->next;  
  42.     Node *e = NULL;  
  43.     Node *q;  
  44.     while(p->next != NULL){  
  45.         q = p->next;  
  46.         p->next = e;  
  47.         e = p;  
  48.         p = q;  
  49.     }  
  50.     p->next = e;  
  51.     head->next = p;  
  52.     return head;  
  53. }  






本節知識點:

1.靜態鏈表究竟是什麼:鏈表就是鏈式存儲的線性表,可是它分爲 動態靜態兩種,所謂動態就是長度不固定,能夠根據狀況自行擴展大小的,靜態鏈表就是長度大小固定的,鏈式存儲的線性表。
2.本節的靜態鏈表和順序表很像(其實和數組也很像), 準確的來講就是利用順序表實現的,只是這個順序表,不是順序排列的,是經過一個next變量,鏈接到下一個變量的。
如圖:

3.唐老師說靜態鏈表是在一些沒有指針的語言中使用的,來實現鏈表的功能,可是我以爲鏈表的最大優點就在於它的伸縮,用多少開闢多少。可是靜態鏈表就偏偏失去了這個優點。依我看, 學習靜態鏈表的目的是學習它這種相似內存管理的算法思想
4.靜態鏈表中值得學習的思想:就是在初始化鏈表的時候,把因此空間都標記成爲可用-1,每次插入數據的時候,都在標記爲可用的空間內挑取,再把-1改爲next。當刪除變量的時候在把next改爲-1,標記爲空間可用。
5.其實仔細想一想 看看,靜態鏈表只是一個思想,爲何這麼說,首先在獲取index的時候,你是順序獲取的,這致使你的next也是連續的,因此他其實就變成了一個順序表。在這裏我想到了一個唐老師的問題,爲何node[0]就能夠看成頭節點,還要再定義一個head變量。唐老師的解答是:順序得到index的時候每次都要遍歷太浪費時間了,因此最好應該在同一塊空間再定義一個鏈表,來保存這些空閒空間,而後這樣就須要兩個鏈表的頭節點了,因此須要一個head。而後讓node[0]一會是空閒鏈表的鏈表頭節點,一會是真實保存數據的鏈表的頭節點。當插入的時候,只須要在那個空閒鏈表取空間就能夠了,提升了算法的效率。
PS1:對於node[0]我真的想說,其實讓node[0]看成頭節點的使用真的很方便,比head方便不少,僅僅是我的體會。
PS2:對於兩個鏈表的那個算法,我以爲若是仍是順序在鏈表中得到index,依然沒有解決這個index是有順序的且順序是固定的問題。這裏的順序是指的是那個空閒鏈表的順序。因此說這僅僅是一個思想。

6. 本節最重要的知識點也是最大的難點:對於柔性數組的描述。
對於柔性數組的結構以下:
[cpp]  view plain copy
  1. typedef struct _tag_StaticList  //由於靜態鏈表是基於順序表改寫的  這個就是順序表中描述順序表的那個結構   
  2. {  
  3.     int capacity;           //靜態鏈表的大小是固定的  這是鏈表的容量   
  4.     StaticListNode head;    //鏈表  頭節點    
  5.     StaticListNode node[];  //利用柔性數組 建立靜態鏈表   
  6. }StaticList;  

而後:給柔性數組開闢空間
[cpp]  view plain copy
  1. StaticList* ret = NULL;   
  2. ret = (StaticList*)malloc( sizeof(StaticList)*1 + sizeof(StaticListNode)*(capacity+1) );  
其實柔性數組就是以數組的方式訪問內存。對於 StaticList  ret這個結構體的大小是不包括StaticLIstNode node[]的,StaticLIstNode node[]是沒有大小的,StaticLIstNode node[0]訪問的內存是StaticList  ret這個結構體後面的第一個內存,StaticLIstNode node[1]訪問的內存是StaticList  ret這個結構體後面的第二個內存等等。
PS:StaticLIstNode node[]這個結構究竟是個什麼結構,很差說,不是數組,也不是指針。就把它看成爲了柔性數組而產生的結構吧!!!

本節代碼:

StaticList.c:
[cpp]  view plain copy
  1. /**************************************************************************************************************  
  2. 文件名:StaticList.c 
  3. 頭文件:StaticList.h 
  4. 時間:2013/08/15 
  5. 做者:Hao 
  6. 功能:能夠複用的 帶有增 刪  改 查 的靜態鏈表  
  7. **************************************************************************************************************/  
  8. #include <stdio.h>  
  9. #include <malloc.h>  
  10. #include <string.h>  
  11. #include "StaticList.h"  
  12.   
  13. #define AVAILABLE -1  
  14.   
  15. typedef struct _tag_StaticListNode  //這個是靜態鏈表的結構   用來保存鏈表元素的   
  16. {  
  17.     unsigned int data;   //這個是爲了複用  保存數據地址的   
  18.     int next;            //這個是用來保存下一個節點位置的   
  19. }StaticListNode;           
  20.   
  21. typedef struct _tag_StaticList  //由於靜態鏈表是基於順序表改寫的  這個就是順序表中描述順序表的那個結構   
  22. {  
  23.     int capacity;           //靜態鏈表的大小是固定的  這是鏈表的容量   
  24.     StaticListNode head;    //鏈表  頭節點    
  25.     StaticListNode node[];  //利用柔性數組 建立靜態鏈表   
  26. }StaticList;  
  27.   
  28. /**************************************************************************************************************  
  29. 函數名 : Creat_StaticList 
  30. 函數功能:建立一個靜態鏈表使用的空間 
  31. 具體數據: StaticList這個結構是描述靜態鏈表的結構    StaticListNode這個結構纔是真正的靜態鏈表的元素 
  32.            每個靜態鏈表的元素都是由兩個部分組成的 一個是數據data(即保存的地址)  另外一個是下一個鏈表 
  33.            元素的位置next     
  34.            對於StaticList這個結構中的數據是capacity是靜態鏈表的容量 head是鏈表的頭節點 
  35.            node[0]也是頭節點 node[]是柔性數據  這裏面保存的纔是真的鏈表內容  
  36. 參數: int capacity  鏈表容量  正確範圍 0到無窮大  當爲0的時候鏈表中僅僅有一個node[0]頭節點  
  37. 返回值:StaticList* ret 返回描述靜態鏈表的結構 StaticList的地址   (SList*這個是爲了封裝) 
  38. **************************************************************************************************************/   
  39. SList* Creat_StaticList(int capacity)  
  40. {  
  41.     int i=0;   
  42.     StaticList* ret = NULL;   
  43.     if( capacity >= 0) //參數合法性檢測  必定要大於等於0   若是capacity爲0 是給node[0]開闢空間  node[0]是鏈表頭節點   
  44.     {  
  45.         ret = (StaticList*)malloc( sizeof(StaticList)*1 + sizeof(StaticListNode)*(capacity+1) );  
  46.     }  
  47.     if(NULL != ret)  //判斷malloc是否成功 內存是否分配成功   
  48.     {  
  49.         ret -> capacity = capacity; //靜態鏈表的容量   
  50.         ret -> head.data = 0;   //頭節點中保存的 鏈表長度   初始化爲0   
  51.         ret -> head.next = 0;   //頭節點中保存的  鏈表下一個節點的位置  初始化爲NULL  
  52.         for(i=1; i<=capacity; i++)  //把鏈表中從node[1]開始 到node[capacity]中的next都標誌爲可用   
  53.         {  
  54.             ret -> node[i].next =  AVAILABLE; //這個在插入函數的時候有用   
  55.         }   
  56.           
  57.     }  
  58.     return (SList*)ret;  
  59.        
  60. }   
  61.   
  62. /**************************************************************************************************************  
  63. 函數名:Destroy_StaticList 
  64. 函數功能:釋放StaticList結構開闢的內存  
  65. 參數:StaticList* Static_List  (SList* Static_List這個是爲了 封裝) 
  66. 返回值:void  
  67. **************************************************************************************************************/   
  68. void Destroy_StaticList(SList* Static_List)  
  69. {  
  70.     free(Static_List); //釋放靜態鏈表建立的內存空間   
  71. }  
  72.   
  73. /************************************************************************************************************** 
  74. 函數名: Get_Lenth 
  75. 函數功能:返回靜態鏈表長度 
  76. 參數:StaticList* Static_List    (SList* List爲了封裝) 
  77. 返回值:成功 int Static_List -> head.data 靜態鏈表使用的長度   失敗返回 0  
  78. **************************************************************************************************************/  
  79. int Get_Lenth(SList* List)   
  80. {  
  81.     StaticList* Static_List = (StaticList*)List;  
  82.     int ret = 0;  
  83.     if(NULL != Static_List)  
  84.     {  
  85.         ret = Static_List -> head.data; //靜態鏈表的長度   
  86.     }  
  87.     return ret;  
  88. }  
  89.   
  90. /************************************************************************************************************** 
  91. 函數名:Get_Capacity 
  92. 函數功能:返回靜態鏈表的容量 
  93. 參數:StaticList* Static_List  (SList* List爲了封裝) 
  94. 返回值:成功返回 int Static_List -> capacity 靜態鏈表的容量  失敗返回 0  
  95. **************************************************************************************************************/  
  96. int Get_Capacity(SList* List)   
  97. {  
  98.     StaticList* Static_List = (StaticList*)List;  
  99.     int ret = 0;  
  100.     if(NULL != Static_List)  
  101.     {  
  102.         ret = Static_List -> capacity; //靜態鏈表的容量   
  103.     }  
  104.     return ret;  
  105. }  
  106.   
  107. /************************************************************************************************************** 
  108. 函數名: Clear_StaticList 
  109. 函數功能:重置靜態鏈表 
  110. 參數:StaticList* Static_List  (SList* List爲了封裝) 
  111. 返回值:成功返回1  失敗返回0  
  112. **************************************************************************************************************/  
  113. int Clear_StaticList(SList* List)  
  114. {  
  115.     StaticList* Static_List = (StaticList*)List;  
  116.     int i = 0;  
  117.     int ret = 0;  
  118.     if(NULL != Static_List)  
  119.     {  
  120.         Static_List -> head.data = 0;  
  121.         Static_List -> head.next = 0;  
  122.         for(i=1; i<=Static_List -> capacity; i++)    
  123.         {  
  124.             Static_List -> node[i].next =  AVAILABLE;    
  125.         }   
  126.         ret = 1;  
  127.     }   
  128.     return ret;  
  129. }  
  130.   
  131. /************************************************************************************************************** 
  132. 函數名: Add_StaticList 
  133. 函數功能: 在鏈表中的pos位置處插入一個鏈表元素   pos的規則跟上節單鏈表的規則同樣 0和1爲頭插法  無窮大爲尾插法 
  134.            node[0]是鏈表頭節點  實際上是head的一箇中間變量  使用node[0]真的很方便 此處記得更新頭節點 
  135. 參數:SList* List 要插入的鏈表地址    SListNode* Node要插入的數據地址   int pos插入的位置  
  136. 返回值:返回1說明插入成功  返回0說明插入失敗  
  137. **************************************************************************************************************/  
  138. int Add_StaticList(SList* List, SListNode* Node, int pos)  
  139. {  
  140.     StaticList* Static_List = (StaticList*)List;  
  141.     StaticListNode*  node =  (StaticListNode*)Node;  
  142.     int ret = 0;  
  143.     int num = 0;  
  144.     int index = 0;  
  145.     int i = 0;  
  146.     ret = (NULL != Static_List)&&(NULL != node);  
  147.     ret = ret&&(Static_List->head.data+1 <= Static_List->capacity)&&(pos >= 0);   
  148.       
  149.     if(ret)  //參數合法性檢測成功   
  150.     {  
  151.         for(i=1; i<=Static_List->capacity; i++) //輪詢得到可用的位置index   
  152.         {  
  153.             if(-1 == Static_List->node[i].next)  
  154.             {  
  155.                 index = i;  
  156.                 break;  
  157.             }  
  158.         }  
  159.         Static_List->node[index].data = (unsigned int)node; //保存鏈表中的數據   
  160.         Static_List->node[0] = Static_List->head;  //此時node[0]變成了鏈表頭節點   
  161.   
  162.         for(i=1; (i < pos)&&(0 != Static_List->node[num].next); i++)  
  163.         {  
  164.             num =  Static_List->node[num].next;  
  165.         }  
  166.         Static_List->node[index].next = Static_List->node[num].next;  
  167.         Static_List->node[num].next = index;  
  168.           
  169.         Static_List->node[0].data++;  
  170.         Static_List->head = Static_List->node[0];//更新鏈表頭節點   
  171.     }  
  172.     return ret;   
  173. }   
  174.   
  175.   
  176. /************************************************************************************************************** 
  177. 函數名: Get_StaticListNode 
  178. 函數功能:得到pos位置處的數據  pos的規則跟單向鏈表同樣 
  179.           範圍是 0  到   head->data  0是頭節點 
  180. 參數:  SList* List 要插入的鏈表地址     int pos插入的位置  
  181. 返回值: 成功返回pos位置處的數據  失敗返回NULL  
  182. **************************************************************************************************************/  
  183. SListNode* Get_StaticListNode(SList* List, int pos)  
  184. {  
  185.     SListNode* ret = NULL;  
  186.     int i = 0;  
  187.     int num = 0;  
  188.     StaticList* Static_List = (StaticList*)List;   
  189.     if( (NULL != Static_List) && (pos <= Static_List->head.data) && (pos >= 0) )  
  190.     {  
  191.         Static_List->node[0] = Static_List->head;   
  192.         for(i=0; i<pos; i++)  
  193.         {  
  194.             num = Static_List->node[num].next;  
  195.         }  
  196.         ret = (SListNode*)Static_List->node[num].data;   
  197.     }  
  198.     return ret;  
  199. }   
  200.   
  201.   
  202. /************************************************************************************************************** 
  203. 函數名: Del_StaticListNode 
  204. 函數功能:刪除pos位置處的數據  pos的規則跟單向鏈表同樣 
  205.           範圍是 1  到   head->data  0是頭節點 不能刪除 
  206. 參數: SList* List 要插入的鏈表地址     int pos刪除的位置 
  207. 返回值:成功返回 pos位置的數據 (目的在於:由於此數據通常是數據的地址  便於釋放內存)    失敗返回NULL  
  208. **************************************************************************************************************/  
  209. SListNode* Del_StaticListNode(SList* List, int pos)  
  210. {  
  211.     SListNode* ret = NULL;  
  212.     int i = 0;  
  213.     int num = 0;  
  214.     int temp = 0;   
  215.     StaticList* Static_List = (StaticList*)List;   
  216.     if( (NULL != Static_List) && (pos <= Static_List->head.data) && (pos > 0) )  
  217.     {  
  218.         Static_List->node[0] = Static_List->head;   
  219.         for(i=1; i<pos; i++)//得找到要刪除的那個節點的上一個   
  220.         {  
  221.             num = Static_List->node[num].next;  
  222.         }  
  223.         temp = Static_List->node[num].next;  
  224.         Static_List->node[num].next = Static_List->node[temp].next;  
  225.           
  226.         Static_List->node[0].data--;  
  227.         Static_List->head = Static_List->node[0]; //更新鏈表頭節點   
  228.           
  229.         Static_List->node[temp].next = AVAILABLE; //把刪除的節點標誌爲可用節點   
  230.           
  231.         ret = (SListNode*)Static_List->node[temp].data;    
  232.     }  
  233.     return ret;  
  234.       
  235. }  


StaticList.h:
[cpp]  view plain copy
  1. #ifndef __STATICLIST_H__  
  2. #define __STATICLIST_H__   
  3.   
  4. typedef void SList;  
  5. typedef void SListNode;  
  6.   
  7. SList* Creat_StaticList(int capacity);  
  8. void Destroy_StaticList(SList* Static_List);  
  9. int Get_Lenth(SList* List);  
  10. int Get_Capacity(SList* List);  
  11. int Clear_StaticList(SList* List);  
  12. int Add_StaticList(SList* List, SListNode* Node, int pos);  
  13. SListNode* Get_StaticListNode(SList* List, int pos);  
  14. SListNode* Del_StaticListNode(SList* List, int pos);  
  15.   
  16. #endif  

main.c:
[cpp]  view plain copy
  1. #include <stdio.h>  
  2. #include <malloc.h>  
  3. #include <string.h>  
  4. #include "StaticList.h"  
  5.   
  6. int main()  
  7. {  
  8.     SList* list = Creat_StaticList(10);  
  9.     int *f = 0;  
  10.     int i = 0;  
  11.     int a = 1;  
  12.     int b = 2;  
  13.     int c = 3;  
  14.     int d = 4;  
  15.     int e = 5;  
  16.       
  17.     Add_StaticList(list, &a, 0);  
  18.     Add_StaticList(list, &b, 0);  
  19.     Add_StaticList(list, &c, 0);  
  20.     Add_StaticList(list, &d, 0);  
  21.       
  22.     for(i=1; i<=Get_Lenth(list); i++)  
  23.     {  
  24.         f=(int* )Get_StaticListNode(list, i);  
  25.         printf("%d\n",*f);  
  26.     }  
  27.        
  28.     Add_StaticList(list, &e, 2);  
  29.     printf("\n");  
  30.     for(i=1; i<=Get_Lenth(list); i++)  
  31.     {  
  32.         f=(int* )Get_StaticListNode(list, i);  
  33.         printf("%d\n",*f);  
  34.     }   
  35.       
  36.     printf("\n");  
  37.     f=(int* )Del_StaticListNode(list, 4);  
  38.     printf("del %d\n",*f);  
  39.     printf("\n");  
  40.     for(i=1; i<=Get_Lenth(list); i++)  
  41.     {  
  42.         f=(int* )Get_StaticListNode(list, i);  
  43.         printf("%d\n",*f);  
  44.     }   
  45.     Destroy_StaticList(list);  
  46.     return 0;  
  47. }  

本節知識點:

1.爲何選擇循環鏈表:由於有不少生活中結構是循環的,是單鏈表解決不了的,好比星期、月份、24小時,對於這些循環的數據,循環鏈表就體現出它的優點了。算法

2.循環鏈表的結構:數組

循環鏈表就是從頭結點後面開始,尾節點的next再也不是NULL了,而是頭結點後面的第一個鏈表元素,如上圖。安全

3.如何建立一個循環鏈表數據結構

步驟一:dom

步驟二:ide

不管是頭插法,仍是尾插法都沒有關係,均可以建立完成這個循環鏈表。函數

4.如何將一個單向鏈表改寫成一個循環鏈表學習

   第一步 (改寫插入函數):

   a.把插入位置pos的容許範圍改爲0~~~無窮大

[cpp]  view plain copy
  1. ret=( NULL != node) && ( NULL != Node) && (pos >= 0);  

   b.把兩種方式的頭插法狀況加入程序,第一種是pos值爲0和1的狀況,如圖:

   

   這種狀況分爲兩部:先把node插入到head和第一個元素直接,而後再把鏈表尾指向node元素(node表示插入元素)。

   代碼以下:

[cpp]  view plain copy
  1. if(node == (CircleListNode* )head)   
  2. {  
  3.     Last =(CircleListNode* )Get_CircleListNode(lhead, lhead->length); //得到鏈表最後一個元素   
  4.     Last->next = Node; //把頭插法的數據鏈接到 鏈表的最後一個元素的後面   
  5. }  

   頭插法的第二種狀況,是循環鏈表,循環了一圈回來了,與第一種不一樣的是此時插入的相對位置和第一種的相對位置不同。(其實這種方法跟普通插入是同樣的)  如圖:

   第二步  (改寫刪除函數):

   a.也是把pos值的取值範圍改爲0  到 無窮大,可是同時記得判斷length要大於0 ,要保證鏈表中有數據,否則刪什麼呀~~~~

[cpp]  view plain copy
  1. if(( NULL != lhead) && (pos > 0) && (lhead->length>0))  

   b.對於刪除第一個元素有兩種狀況 這裏是難點:首先要在刪除鏈表元素的 前面 判斷是否要刪除第一個元素(此時的狀況是pos爲1的狀況),而後刪除鏈表元素,再判斷是不是刪除第一個元素的第二種狀況(鏈表循環一圈後,到達鏈表第一個元素,此時元素的前一個鏈表再也不是head頭結點了)。如圖:


 代碼以下:

[cpp]  view plain copy
  1. if(node == (CircleListNode* )head)  
  2. {  
  3.     Last =(CircleListNode* )Get_CircleListNode(lhead, lhead->length);  
  4. }  
  5.           
  6. ret = node->next;  
  7. node->next = ret->next;     
  8. /*判斷是否是循環了一圈後回來的狀況 */  
  9. if((first == ret) &&(NULL == Last))  
  10. {  
  11.     Last =(CircleListNode* )Get_CircleListNode(lhead, lhead->length);  
  12. }  
  13. /*判斷是否要刪除鏈表中的第一個元素*/  
  14. if( Last != NULL )  
  15. {  
  16.     Last->next = ret->next;   
  17.     lhead->head.next = ret->next;  
  18. }  

圖中紅筆的代碼是:

[cpp]  view plain copy
  1. ret = node->next;  
  2. node->next = ret->next;     

圖中藍筆的代碼是:

[cpp]  view plain copy
  1. if( Last != NULL )  
  2. {  
  3.     Last->next = ret->next;   
  4.     lhead->head.next = ret->next;  
  5. }  

   c.當length爲0的是,即鏈表長度爲0的時候,記得給頭結點的next賦值爲NULL

  第三步 (改寫得到鏈表元素函數)

  a.記得把pos給成 0 到 無窮大,而後判斷length鏈表長度是否爲0 ,若是爲0 就不能獲取。

5.遊標的引入:

在循環鏈表中通常能夠定義一個遊標,對於這樣一個封裝好的可複用循環鏈表,定義一個遊標是十分方便的。例如:若是想依次得到鏈表中的每個元素,利用get函數,太太低效了O(n2),想一想利用這樣一個遊標去遍歷的話,複雜度僅僅是O(n)。還有就是在循環鏈表中,遊標能夠在鏈表中進行轉圈,例如:能夠解決約瑟夫環問題。


6.指定刪除鏈表中某一個元素的函數CircleListNode* CircleList_Del(CircleList* head,CircleListNode* node),其實也不是很高效,得到了當前遊標的值的時候,再去調用CircleList_Del函數,這個輪詢函數得到了pos,再去調用Del_CircleListNode而後又遍歷了一邊,把複雜的搞到了O(n2)。其實徹底能夠在找到pos的時候直接刪除掉這個鏈表元素,這樣的複雜度是O(n)。

7.我還以爲得到當前遊標得值的函數CircleList_Slider的返回值有些問題,我以爲若是返回的是當前遊標的上一個鏈表元素的值會更好,由於這個是一個單向鏈表,若是獲得了上一個鏈表元素的值,就能夠經過遊標實現,刪除啊,插入啊等高效的操做了。

本節代碼:

CricleList.c:

[cpp]  view plain copy
  1. /******************************************************************************************************* 
  2. 文件名:CircleList.c 
  3. 頭文件:CircleList.h  
  4. 時間: 2013/08/17 
  5. 做者: Hao 
  6. 功能:  能夠複用 帶有增 刪 改 查 功能的循環鏈表 
  7. 難道: 1.typedef struct Str_CircleList CircleListNode;  //這個結構體是鏈表的真身  
  8.         struct Str_CircleList   //每個鏈表元素的結構都會包含這個結構  由於當給鏈表元素強制類型  
  9.         {                     //轉換成(CircleListNode* )的時候  其實就是要開始對每一個元素中的 CircleListNode進行賦值了  
  10.             CircleListNode* next; 
  11.         };  
  12.         這個鏈表結構在鏈表元素中起到的做用 是本節的難點  
  13.         2.切記一個問題  就是已是鏈表中元素的 千萬不要再往鏈表中添加了 不然鏈表必定出現無窮的錯誤  
  14.         3.對於pos值的問題  add、get、del三個函數中 的鏈表都是 從1開始的到length  0是鏈表頭  
  15.                           在add函數中pos爲0的時候是和pos爲1的狀況是同樣的  都是頭插法  0~~~~~無窮大  
  16.                           在get函數中pos爲0的時候是得到鏈表頭 地址      0~~~~~length  
  17.                           在del函數中pos爲0的時候是無效的 del失敗       1~~~~~length  
  18. *******************************************************************************************************/  
  19. #include <stdio.h>  
  20. #include <stdlib.h>  
  21. #include <malloc.h>  
  22. #include "CircleList.h"  
  23.   
  24. typedef struct str_list_head  //這個是鏈表頭 其實也能夠看成一個沒有前驅的 鏈表元素 元素的內容是鏈表長度   
  25. {  
  26.     //CircleListNode* next;  
  27.     CircleListNode head; //這個參數要特別重視 每個鏈表元素結構的第一個參數必定是 CircleListNode  
  28.                        //由於在尋找鏈表元素後繼的時候 其實就是將鏈表元素強制類型轉換成 CircleListNode*  而後給next進行賦值 其實就是給 CircleListNode變量賦值   
  29.     CircleListNode* slider;   
  30.     int length; //鏈表長度   
  31. }list_head;  
  32.   
  33. /******************************************************************************************************* 
  34. 函數名: Creat_CircleListHead 
  35. 函數功能:建立一個鏈表的鏈表頭 並給鏈表頭分配空間 
  36. 參數: void 
  37. 返回值:ret 成功返回鏈表頭地址  失敗返回NULL  
  38. *******************************************************************************************************/  
  39. CircleList* Creat_CircleListHead(void)  
  40. {  
  41.     list_head* ret = NULL;  
  42.     ret = (list_head* )malloc( sizeof(list_head)*1 );  
  43.     if(NULL != ret) //malloc分配成功   
  44.     {  
  45.         ret->length = 0;  
  46.         //ret -> next = NULL;  
  47.         ret->head.next = NULL;  
  48.         ret->slider = NULL;  
  49.     }  
  50.     return (CircleList* )ret;   
  51. }  
  52.   
  53. /******************************************************************************************************* 
  54. 函數名:Destroy_CircleListHead 
  55. 函數功能:釋放一個鏈表頭指針  
  56. 參數:CircleList* head 鏈表頭指針  
  57. 返回值: ret 釋放成功返回1  釋放失敗返回0  
  58. *******************************************************************************************************/  
  59. int Destroy_CircleListHead(CircleList* head)  
  60. {  
  61.     int ret = 0;   
  62.     list_head* lhead = (list_head* )head;  
  63.     if( NULL != lhead )  
  64.     {  
  65.         free(lhead);  
  66.         ret = 1;  
  67.     }  
  68.     return ret;  
  69. }  
  70.   
  71. /******************************************************************************************************* 
  72. 函數名:Get_Length 
  73. 函數功能:得到鏈表的長度  
  74. 參數: CircleList* head 鏈表頭指針  
  75. 返回值: ret 成功返回鏈表長度  失敗返回0  
  76. *******************************************************************************************************/  
  77. int Get_Length(CircleList* head)   
  78. {  
  79.     int ret = 0;  
  80.     list_head* lhead = (list_head* )head;  
  81.     if( NULL != lhead )  
  82.     {  
  83.         ret = lhead -> length;  
  84.     }     
  85.     return ret;  
  86. }  
  87.   
  88. /******************************************************************************************************* 
  89. 函數名:Clean_CircleListHead 
  90. 函數功能:   清空鏈表  
  91. 參數: CircleList* head 鏈表頭指針  
  92. 返回值:ret 成功返回1 失敗返回0  
  93. *******************************************************************************************************/  
  94. int Clean_CircleListHead(CircleList* head)   
  95. {  
  96.     int ret = 0;  
  97.     list_head* lhead = (list_head* )head;  
  98.     if( NULL != lhead )  
  99.     {  
  100.         lhead -> length = 0;  
  101.         //lhead  -> next = NULL;  
  102.         lhead -> head.next = NULL;  
  103.         lhead->slider = NULL;  
  104.         ret = 1;  
  105.     }     
  106.     return ret;  
  107. }  
  108.   
  109. /******************************************************************************************************* 
  110. 函數名:Add_CircleList 
  111. 函數功能:往鏈表裏面添加一個鏈表元素 若是pos的值是0(就是鏈表頭)和1(鏈表的第一元素 鏈表元素個數是從1開始算的)都是頭插法 
  112.           pos的值大於鏈表長度是尾插法  這裏面pos值得注意的是 i=1 pos爲a的時候 是把鏈表元素插入第a個元素的位置  
  113.           當i=0 pos爲a的時候 是把鏈表元素插入 第a個元素位置的後面    切忌:這裏面0位置是鏈表頭指針 從1開始是鏈表元素  
  114. 參數:   CircleList* head鏈表頭指針    CircleListNode* Node插入元素的指針(被強制類型轉化成CircleListNode*)  int pos 插入位置  
  115.          pos的有效值範圍是 從0到無窮大   
  116. 返回值: ret 插入成功返回1  插入失敗返回0  
  117. *******************************************************************************************************/  
  118. int Add_CircleList(CircleList* head, CircleListNode* Node, int pos)  
  119. {  
  120.     int ret = 0;  
  121.     int i = 0;  
  122.     list_head* lhead = (list_head* )head;  
  123.     CircleListNode* node = (CircleListNode* )head;  
  124.     CircleListNode* Last = NULL;  
  125.     ret=( NULL != node) && ( NULL != Node) && (pos >= 0);  
  126.     if(1 == ret)  
  127.     {  
  128.         for(i=1; ( (i<pos) && (node->next != NULL) ); i++)  
  129.         {  
  130.             node = node->next;  
  131.         }  
  132.         Node -> next = node -> next;  
  133.         node -> next = Node;  
  134.         if(lhead->length == 0)//第一次插入元素的時候把遊標 指向這個元素    
  135.         {  
  136.             lhead->slider = Node;  
  137.         }  
  138.         lhead -> length++; //這個必定要在後面調用 lhead->length值的前面更新   
  139.         /*判斷是否爲頭插法  所謂頭插法 就是pos爲0和1的狀況 其實也就是沒有進for循環的狀況  剩下的不管pos爲多少  進入多少次循環都沒有頭插法*/  
  140.         if(node == (CircleListNode* )head)   
  141.         {  
  142.             Last =(CircleListNode* )Get_CircleListNode(lhead, lhead->length); //得到鏈表最後一個元素   
  143.             Last->next = Node; //把頭插法的數據鏈接到 鏈表的最後一個元素的後面   
  144.         }  
  145.           
  146.     }  
  147.     return ret;  
  148. }  
  149.   
  150. /******************************************************************************************************* 
  151. 函數名:Get_CircleListNode 
  152. 函數功能:得到鏈表中第pos個元素位置的鏈表元素 鏈表是從1開始的  0是鏈表頭   pos爲0的時候表示get鏈表頭  
  153. 參數: CircleList* head鏈表頭指針    int pos得到鏈表元素的位置  pos的有效取值範圍是 1 到  length  0是鏈表頭  
  154. 返回值: CircleListNode*類型 第pos個鏈表元素的地址  
  155. *******************************************************************************************************/  
  156. CircleListNode* Get_CircleListNode(CircleList* head, int pos)  
  157. {  
  158.     int ret = 0;  
  159.     int i = 0;  
  160.     list_head* lhead = (list_head* )head;  
  161.     /*原本pos應該是有上限的  可是變成了循環鏈表pos理論上說就能夠無窮大了  可是get函數應該是在鏈表中有值的狀況下才成立的 即(lhead->length>0)*/   
  162.     ret=( NULL != lhead) && (pos >= 0) && (lhead->length>0);   
  163.     if(1 == ret)  
  164.     {  
  165.         CircleListNode* node = (CircleListNode* )head;  
  166.         for(i=0; i<pos; i++) //執行 pos次   獲得的是第pos位置的node   
  167.         {  
  168.             node = node->next;  
  169.         }     
  170.         return (CircleListNode*)node;  
  171.     }  
  172.     return NULL;  
  173. }  
  174.   
  175. /******************************************************************************************************* 
  176. 函數名:Del_CircleListNode 
  177. 函數功能:刪除鏈表中第pos位置的鏈表元素  
  178. 參數: CircleList* head鏈表頭指針    int pos刪除鏈表元素的位置  pos是刪除的鏈表元素的位置 跟get和add中的 
  179.        pos是配套的  有效取值範圍依然是 1到 length  在這個函數裏面因爲不能刪除鏈表頭 因此pos爲0的時候無效  
  180. 返回值: CircleListNode* ret這個返回值很重要 由於這個刪除僅僅是把鏈表元素踢出了鏈表 並無free開闢的內存 
  181.          應該經過這個返回的地址free  釋放內存 
  182.          刪除成功返回 刪除鏈表元素的地址   刪除失敗返回 NULL  
  183. *******************************************************************************************************/  
  184. CircleListNode* Del_CircleListNode(CircleList* head, int pos)  
  185. {  
  186.     CircleListNode* ret = NULL;  
  187.     CircleListNode* Last = NULL;  
  188.     int i = 0;  
  189.     list_head* lhead = (list_head* )head;  
  190.     CircleListNode* first = lhead->head.next;  
  191.       
  192.     if(( NULL != lhead) && (pos > 0) && (lhead->length>0))  
  193.     {  
  194.         CircleListNode* node = (CircleListNode* )head;  
  195.         for(i=1; i<pos; i++)//執行 pos次   獲得的是第pos位置的node  這個方法行不通   
  196.         {                   //由於要想刪除第pos位置的node 應該先找到它上一個鏈表元素   
  197.             node = node->next; //因此這裏面i=1 比get函數少執行了一次  獲得第pos-1位置的node   
  198.         }  
  199.         /*判斷是否是 pos爲1的 狀況刪除頭節點後面的第一個元素(這個是沒有進入for循環的)  跟循環一圈後的狀況不同  */  
  200.         /*循環一圈的是進入for循環的狀況   此時的node再也不是head了 而是鏈表最後一個元素*/   
  201.         if(node == (CircleListNode* )head)  
  202.         {  
  203.             Last =(CircleListNode* )Get_CircleListNode(lhead, lhead->length);  
  204.         }  
  205.           
  206.         ret = node->next;  
  207.         node->next = ret->next;     
  208.         /*判斷是否是循環了一圈後回來的狀況 */  
  209.         if((first == ret) &&(NULL == Last))  
  210.         {  
  211.             Last =(CircleListNode* )Get_CircleListNode(lhead, lhead->length);  
  212.         }  
  213.         /*判斷是否要刪除鏈表中的第一個元素*/  
  214.         if( Last != NULL )  
  215.         {  
  216.             Last->next = ret->next;   
  217.             lhead->head.next = ret->next;  
  218.         }  
  219.         if( lhead->slider == ret)//若是刪除的元素偏偏就是遊標指向的元素  要把遊標日後面移動一位   
  220.         {  
  221.             lhead->slider = ret->next;  
  222.         }  
  223.         lhead->length--; //這個必定要寫在 Get_CircleListNode 後面 否則的話 pos就爲0了   
  224.         /*判斷鏈表是否 減到了空  若是鏈表中再也不有元素 就把head.next賦值爲NULL*/  
  225.         /*單向鏈表不須要這個的緣由 是由於單向鏈表的最後一個元素的next就是NULL 而雙向鏈表沒有NULL的了*/  
  226.         if(0 == lhead->length)  
  227.         {  
  228.             lhead->head.next = NULL;  
  229.             lhead->slider = NULL;   
  230.         }  
  231.           
  232.     }  
  233.     return (CircleListNode*)ret;  
  234. }  
  235.   
  236. /******************************************************************************************************* 
  237. 函數名: CircleList_Slider 
  238. 函數功能:得到當前遊標指向的數據 
  239. 參數: CircleList* head 
  240. 返回值:成功返回 CircleListNode* ret  失敗返回NULL  
  241. *******************************************************************************************************/  
  242. CircleListNode* CircleList_Slider(CircleList* head)  
  243. {  
  244.     CircleListNode* ret = NULL;  
  245.     list_head* lhead = (list_head* )head;  
  246.     if( (NULL != lhead)&&(NULL != lhead->slider) )//保證slider是有效的   
  247.     {  
  248.         ret = lhead->slider;  
  249.     }  
  250.     return ret;  
  251. }  
  252.   
  253. /******************************************************************************************************* 
  254. 函數名: CircleList_Reset 
  255. 函數功能:重置遊標 讓遊標指向head頭節點後面的第一個元素  
  256. 參數: CircleList* head 
  257. 返回值:成功返回 當前遊標的指向CircleListNode* ret  失敗返回NULL  
  258. *******************************************************************************************************/  
  259. CircleListNode* CircleList_Reset(CircleList* head)  
  260. {  
  261.     CircleListNode* ret = NULL;  
  262.     list_head* lhead = (list_head* )head;  
  263.     if(NULL != lhead)  
  264.     {  
  265.         lhead->slider = lhead->head.next;  
  266.         ret = lhead->slider;  
  267.     }  
  268.     return ret;  
  269. }  
  270.   
  271. /******************************************************************************************************* 
  272. 函數名: CircleList_Next 
  273. 函數功能:使遊標指向下一個元素  
  274. 參數: CircleList* head 
  275. 返回值:成功返回 前遊標的指向CircleListNode* ret  失敗返回NULL  
  276. *******************************************************************************************************/  
  277. CircleListNode* CircleList_Next(CircleList* head)  
  278. {  
  279.     CircleListNode* ret = NULL;  
  280.     list_head* lhead = (list_head* )head;  
  281.     if((NULL != lhead)&&(NULL != lhead->slider)) //保證遊標是有效的   
  282.     {  
  283.         ret = lhead->slider;  
  284.         lhead->slider = ret->next;   
  285.     }  
  286.     return ret;  
  287. }  
  288.   
  289. /******************************************************************************************************* 
  290. 函數名: CircleList_Del 
  291. 函數功能:刪除鏈表中的某個指定元素  
  292. 參數: CircleList* head   CircleListNode* node爲指定的元素  
  293. 返回值:成功返回 刪除的鏈表元素  失敗返回NULL  
  294. *******************************************************************************************************/  
  295. CircleListNode* CircleList_Del(CircleList* head,CircleListNode* node)  
  296. {   //這個函數主要是用來刪除遊標的返回值的   
  297.    
  298.     CircleListNode* ret = NULL;  
  299.     list_head* lhead = (list_head* )head;  
  300.     int i=0;   
  301.     if((NULL != head)&&(NULL != node))  
  302.     {  
  303.         CircleListNode* current = (CircleListNode*)lhead;  
  304.         for(i=1; i<=lhead->length; i++)  
  305.         {  
  306.             if(node == current->next)  
  307.             {  
  308.                 ret = current->next;  
  309.                 break;   
  310.             }   
  311.             current = current->next;  
  312.         }  
  313.           
  314.         if(NULL == ret)  //說明沒有找到node   
  315.         {  
  316.             printf("put error!!!\n");   
  317.         }  
  318.         else //找到了node   
  319.         {  
  320.             Del_CircleListNode(lhead,i);   
  321.         }   
  322.     }     
  323.     return ret;//返回刪除的鏈表元素   
  324. }  


CircleList.h:

[cpp]  view plain copy
  1. #ifndef __CircleList_H__  
  2. #define __CircleList_H__  
  3.   
  4. typedef void CircleList;  //這個是爲了 封裝方便   
  5. typedef struct Str_CircleList CircleListNode;  //這個結構體是鏈表的真身   
  6. struct Str_CircleList   //每個鏈表元素的結構都會包含這個結構  由於當給鏈表元素強制類型   
  7. {                     //轉換成(CircleListNode* )的時候  其實就是要開始對每一個元素中的 CircleListNode進行賦值了   
  8.     CircleListNode* next;  
  9. };  
  10.   
  11. CircleList* Creat_CircleListHead(void);  
  12.   
  13. int Destroy_CircleListHead(CircleList* head);  
  14.   
  15. int Get_Length(CircleList* head);  
  16.   
  17. int Clean_CircleListHead(CircleList* head);  
  18.   
  19. int Add_CircleList(CircleList* head, CircleListNode* Node, int pos);  
  20.   
  21. CircleListNode* Get_CircleListNode(CircleList* head, int pos);  
  22.   
  23. CircleListNode* Del_CircleListNode(CircleList* head, int pos);   
  24.   
  25. CircleListNode* CircleList_Del(CircleList* head,CircleListNode* node);  
  26.   
  27. CircleListNode* CircleList_Next(CircleList* head);  
  28.   
  29. CircleListNode* CircleList_Reset(CircleList* head);  
  30.   
  31. CircleListNode* CircleList_Slider(CircleList* head);  
  32.    
  33. #endif  


main.c:

[cpp]  view plain copy
  1. #include <stdio.h>  
  2. #include <stdlib.h>  
  3. #include "CircleList.h"  
  4.   
  5. typedef struct _tag_str  
  6. {  
  7.     CircleListNode head;  
  8.     int i;  
  9. }str;  
  10. int main(int argc, char *argv[])   
  11. {  
  12.     str str1,str2,str3,str4,str5,str6;  
  13.     str *strp;  
  14.     int i=0;  
  15.     str1.i=1;  
  16.     str2.i=2;  
  17.     str3.i=3;  
  18.     str4.i=4;  
  19.     str5.i=5;  
  20.     str6.i=6;  
  21.     CircleList* head;  
  22.     head = Creat_CircleListHead();  
  23.       
  24.     Add_CircleList(head, (CircleListNode*)&str1, 0);  
  25.     Add_CircleList(head, (CircleListNode*)&str2, 0);  
  26.     Add_CircleList(head, (CircleListNode*)&str3, 0);  
  27.     Add_CircleList(head, (CircleListNode*)&str4, 0);  
  28.     Add_CircleList(head, (CircleListNode*)&str5, 5);  
  29.       
  30.     for(i=1; i<=2*Get_Length(head); i++)  
  31.     {  
  32.         strp = (str* )Get_CircleListNode(head, i);  
  33.         printf("%d\n",strp->i);  
  34.     }  
  35.     printf("\n");  
  36.       
  37.     printf("%d\n",Get_Length(head));  
  38.     strp = (str* )Del_CircleListNode(head, 6);  
  39.     printf("%d\n",strp->i);    
  40.       
  41.     printf("%d\n",Get_Length(head));  
  42.     printf("\n");  
  43.     for(i=1; i<=2*Get_Length(head); i++)  
  44.     {  
  45.         strp = (str* )Get_CircleListNode(head, i);  
  46.         printf("%d\n",strp->i);  
  47.     }  
  48.       
  49.     printf("\n");  
  50.     printf("%d\n",Get_Length(head));  
  51.     strp = (str* )Del_CircleListNode(head, 1);  
  52.     printf("%d\n",strp->i);    
  53.       
  54.     printf("%d\n",Get_Length(head));  
  55.     printf("\n");  
  56.     for(i=1; i<=2*Get_Length(head); i++)  
  57.     {  
  58.         strp = (str* )Get_CircleListNode(head, i);  
  59.         printf("%d\n",strp->i);  
  60.     }  
  61.       
  62.       
  63.       
  64.       
  65.     printf("\n");  
  66.     for(i=1; i<=3; i++)  
  67.     {  
  68.         strp = (str* )Del_CircleListNode(head, 1);  
  69.         printf("%d\n",strp->i);  
  70.     }  
  71.       
  72.     /*CircleList_Reset(head); 
  73.     CircleList_Next(head); 
  74.     CircleList_Del(head,(CircleListNode*)&str3); 
  75.     strp = (str* )CircleList_Slider(head); 
  76.     printf("%d\n",strp->i); 
  77.     printf("\n"); 
  78.      
  79.     for(i=1; i<=2*Get_Length(head); i++) 
  80.     { 
  81.         strp = (str* )Get_CircleListNode(head, i); 
  82.         printf("%d\n",strp->i); 
  83.     } 
  84.     printf("\n");*/  
  85.       
  86.   
  87.       
  88.     Destroy_CircleListHead(head);  
  89.     return 0;  
  90. }  



 

 

 

 

本節知識點:

1.爲何選擇雙向鏈表:由於單向鏈表只能一直指向下一個鏈表元素,不能得到前一個元素,若是要進行逆序訪問操做是極其耗時的,因此引入雙向鏈表。
2.雙向鏈表的結構:在單向鏈表的基礎上增長了一個鏈表結構pre,如圖。
注意:鏈表第一個元素的前驅pre不是指向頭結點head,而是指向NULL,鏈表尾結點的後繼next指向NULL
3.如何將一個單向鏈表改爲雙向鏈表:
   第一步 (改變鏈表的結構加入前驅):
[cpp]  view plain copy
  1. struct Str_DLinkList   //每個鏈表元素的結構都會包含這個結構  由於當給鏈表元素強制類型   
  2. {                     //轉換成(DLinkListNode* )的時候  其實就是要開始對每一個元素中的 DLinkListNode進行賦值了   
  3.     DLinkListNode* next;  
  4.     DLinkListNode* pre;  
  5. };  

   第二步 (改寫插入函數):
   對於一個尾插法,如圖:
   
    (1).正常的鏈表插入操做,代碼以下:
[cpp]  view plain copy
  1. for(i=1; ( (i<pos) && (node->next != NULL) ); i++)  
  2. {  
  3.     node = node->next;  
  4. }  
  5. /*此處的node是要插入元素的前一個值  Node是要刪除的值*/   
  6. Node -> next = node -> next;  
  7. node -> next = Node;  
    (2).把剛剛插入的數據的前驅pre跟前一個數據元素相連,代碼以下:
[cpp]  view plain copy
  1. Node->pre = node;  
    對於一個正常插入,如圖:

    (1).正常的鏈表插入操做,代碼以下:
[cpp]  view plain copy
  1. for(i=1; ( (i<pos) && (node->next != NULL) ); i++)  
  2. {  
  3.     node = node->next;  
  4. }  
  5. /*此處的node是要插入元素的前一個值  Node是要刪除的值*/   
  6. Node -> next = node -> next;  
  7. node -> next = Node;  
    (2).先判斷是否是尾插法,若是是尾插法,就像上一個狀況同樣,就不進行這一步的操做了,代碼以下:
[cpp]  view plain copy
  1. if(NULL != Node->next) //判斷是否爲尾插法 若是不是進入以下操做   若是是尾插法  最後一個鏈表元素不看成NULL的前驅   
  2. {  
  3.     Node->next->pre = Node;   
  4. }  
   (3).把剛剛插入的數據的前驅pre跟前一個數據元素相連,代碼以下:
[cpp]  view plain copy
  1. Node->pre = node;  
   對於一個頭插法,如圖:



     (1).正常的鏈表插入操做,代碼以下:
[cpp]  view plain copy
  1. for(i=1; ( (i<pos) && (node->next != NULL) ); i++)  
相關文章
相關標籤/搜索