数据结构学习笔记

2019年12月07日 阅读数:137
这篇文章主要向大家介绍数据结构学习笔记,主要内容包括基础应用、实用技巧、原理机制等方面,希望对大家有所帮助。
 最近在看国嵌唐老师的数据结构视频,以为还不错,因此就把笔记记录下来

本节知识点:

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++)  
  2. {  
  3. &nbs