数据结构之单链表
单链表
好处:①无需一次性定制链表的容量;②插入和删除无需移动数据元素
坏处:①数据元素需要保存后继元素的位置信息;②获取数据元素操作需要顺序访问之前的元素
链表头(表头节点):链表中的第一个节点,包含指向第一个数据元素的指针以及链表自身的一些信息(即链表长度length)
数据节点:链表中的代表数据元素的节点,包含指向下一个数据元素的指针和数据元素的信息
尾节点:链表中的最后一个数据节点,其下一元素指针为空,表示无后继
理解:
1 typedef struct Str_LinkList LinkListNode; //这个结构体是链表的真身 2 struct Str_LinkList //每一个链表元素的结构都会包含这个结构 因为当给链表元素强制类型 3 { //转换成(LinkListNode* )的时候 其实就是要开始对每个元素中的 LinkListNode进行赋值了 4 LinkListNode* next; 5 };
可以复用的单链表:
1、LinkList.c
1 /******************************************************************************************************* 2 文件名:LinkList.c 3 头文件:LinkList.h 4 功能: 可以复用 带有增 删 改 查 功能的单链表 5 难点: 1.typedef struct Str_LinkList LinkListNode; //这个结构体是链表的真身 6 struct Str_LinkList //每一个链表元素的结构都会包含这个结构 因为当给链表元素强制类型 7 { //转换成(LinkListNode* )的时候 其实就是要开始对每个元素中的 LinkListNode进行赋值了 8 LinkListNode* next; 9 }; 10 这个链表结构在链表元素中起到的作用 是本节的难点 11 2.切记一个问题 就是已经是链表中元素的 千万不要再往链表中添加了 否则链表一定出现无穷的错误 12 3.对于pos值的问题 add、get、del三个函数中 的链表都是 从1开始的到length 0是链表头 13 在add函数中pos为0的时候是和pos为1的情况是一样的 都是头插法 0~~~~~无穷大 14 在get函数中pos为0的时候是获得链表头 地址 0~~~~~length 15 在del函数中pos为0的时候是无效的 del失败 1~~~~~length 16 *******************************************************************************************************/ 17 #include <stdio.h> 18 #include <stdlib.h> 19 #include <malloc.h> 20 #include "LinkList.h" 21 22 typedef struct str_list_head //这个是链表头 其实也可以当作一个没有前驱的 链表元素 元素的内容是链表长度 23 { 24 //LinkListNode* next; 25 LinkListNode head; //这个参数要特别重视 每一个链表元素结构的第一个参数一定是 LinkListNode 26 //因为在寻找链表元素后继的时候 其实就是将链表元素强制类型转换成 LinkListNode* 然后给next进行赋值 其实就是给 LinkListNode变量赋值 27 int length; //链表长度 28 }list_head; 29 30 /******************************************************************************************************* 31 函数名: Creat_LinkListHead 32 函数功能:创建一个链表的链表头 并给链表头分配空间 33 参数: void 34 返回值:ret 成功返回链表头地址 失败返回NULL 35 *******************************************************************************************************/ 36 LinkList* Creat_LinkListHead(void) 37 { 38 list_head* ret = NULL; 39 ret = (list_head* )malloc( sizeof(list_head)*1 ); 40 if(NULL != ret) //malloc分配成功 41 { 42 ret -> length = 0; 43 //ret -> next = NULL; 44 ret -> head.next = NULL; 45 } 46 return (LinkList* )ret; 47 } 48 49 /******************************************************************************************************* 50 函数名:Destroy_LinkListHead 51 函数功能:释放一个链表头指针 52 参数:LinkList* head 链表头指针 53 返回值: ret 释放成功返回1 释放失败返回0 54 *******************************************************************************************************/ 55 int Destroy_LinkListHead(LinkList* head) 56 { 57 int ret = 0; 58 list_head* lhead = (list_head* )head; 59 if( NULL != lhead ) 60 { 61 free(lhead); 62 ret = 1; 63 } 64 return ret; 65 } 66 67 /******************************************************************************************************* 68 函数名:Get_Length 69 函数功能:获得链表的长度 70 参数: LinkList* head 链表头指针 71 返回值: ret 成功返回链表长度 失败返回0 72 *******************************************************************************************************/ 73 int Get_Length(LinkList* head) 74 { 75 int ret = 0; 76 list_head* lhead = (list_head* )head; 77 if( NULL != lhead ) 78 { 79 ret = lhead -> length; 80 } 81 return ret; 82 } 83 84 /******************************************************************************************************* 85 函数名:Clean_LinkListHead 86 函数功能: 清空链表 87 参数: LinkList* head 链表头指针 88 返回值:ret 成功返回1 失败返回0 89 *******************************************************************************************************/ 90 int Clean_LinkListHead(LinkList* head) 91 { 92 int ret = 0; 93 list_head* lhead = (list_head* )head; 94 if( NULL != lhead ) 95 { 96 lhead -> length = 0; 97 //lhead -> next = NULL; 98 lhead -> head.next = NULL; 99 ret = 1; 100 } 101 return ret; 102 } 103 104 /******************************************************************************************************* 105 函数名:Add_LinkList 106 函数功能:往链表里面添加一个链表元素 如果pos的值是0(就是链表头)和1(链表的第一元素 链表元素个数是从1开始算的)都是头插法 107 pos的值大于链表长度是尾插法 这里面pos值得注意的是 i=1 pos为a的时候 是把链表元素插入第a个元素的位置 108 当i=0 pos为a的时候 是把链表元素插入 第a个元素位置的后面 切忌:这里面0位置是链表头指针 从1开始是链表元素 109 参数: LinkList* head链表头指针 LinkListNode* Node插入元素的指针(被强制类型转化成LinkListNode*) int pos 插入位置 110 pos的有效值范围是 从0到无穷大 111 返回值: ret 插入成功返回1 插入失败返回0 112 *******************************************************************************************************/ 113 int Add_LinkList(LinkList* head, LinkListNode* Node, int pos) 114 { 115 int ret = 0; 116 int i = 0; 117 list_head* lhead = (list_head* )head; 118 LinkListNode* node = (LinkListNode* )head; 119 ret=( NULL != node) && ( NULL != Node) && (pos >= 0); 120 if(1 == ret) 121 { 122 for(i=1; ( (i<pos) && (node->next != NULL) ); i++) 123 { 124 node = node->next; 125 } 126 Node -> next = node -> next; 127 node -> next = Node; 128 129 lhead -> length++; 130 } 131 return ret; 132 } 133 134 /******************************************************************************************************* 135 函数名:Get_LinkListNode 136 函数功能:获得链表中第pos个元素位置的链表元素 链表是从1开始的 0是链表头 pos为0的时候表示get链表头 137 参数: LinkList* head链表头指针 int pos获得链表元素的位置 pos的有效取值范围是 1 到 length 0是链表头 138 返回值: LinkListNode*类型 第pos个链表元素的地址 139 *******************************************************************************************************/ 140 LinkListNode* Get_LinkListNode(LinkList* head, int pos) 141 { 142 int ret = 0; 143 int i = 0; 144 list_head* lhead = (list_head* )head; 145 ret=( NULL != lhead) && (pos >= 0) && (pos <= lhead->length); 146 if(1 == ret) 147 { 148 LinkListNode* node = (LinkListNode* )head; 149 for(i=0; i<pos; i++) //执行 pos次 得到的是第pos位置的node 150 { 151 node = node->next; 152 } 153 return (LinkListNode*)node; 154 } 155 return NULL; 156 } 157 158 /******************************************************************************************************* 159 函数名:Del_LinkListNode 160 函数功能:删除链表中第pos位置的链表元素 161 参数: LinkList* head链表头指针 int pos删除链表元素的位置 pos是删除的链表元素的位置 跟get和add中的 162 pos是配套的 有效取值范围依然是 1到 length 在这个函数里面由于不能删除链表头 所以pos为0的时候无效 163 返回值: LinkListNode* ret这个返回值很重要 因为这个删除仅仅是把链表元素踢出了链表 并没有free开辟的内存 164 应该通过这个返回的地址free 释放内存 165 删除成功返回 删除链表元素的地址 删除失败返回 NULL 166 *******************************************************************************************************/ 167 LinkListNode* Del_LinkListNode(LinkList* head, int pos) 168 { 169 LinkListNode* ret = NULL; 170 int i = 0; 171 list_head* lhead = (list_head* )head; 172 if(( NULL != lhead) && (pos > 0) && (pos <= lhead->length)) 173 { 174 LinkListNode* node = (LinkListNode* )head; 175 for(i=1; i<pos; i++)//执行 pos次 得到的是第pos位置的node 这个方法行不通 176 { //因为要想删除第pos位置的node 应该先找到它上一个链表元素 177 node = node->next; //所以这里面i=1 比get函数少执行了一次 得到第pos-1位置的node 178 } 179 ret = node->next; 180 node->next = ret->next; 181 182 lhead->length--; 183 } 184 return (LinkListNode*)ret; 185 }
2、LinkList_h
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
3、main.c
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 }