通用双向链表C实现
注意:
1.封装函数,使用者不需要要了解底层细节,我们把内部使用的函数用static 声明.
2.我们存储的数据类型为void *, 由用户决定函数类型
3.代码尽可能减少代码量
#ifndef DLINK_H #define DLINK_H //用户应该只操作Dlink,避免操作节点.不需要了解细节 struct _DLink; typedef struct _DLink DLink; DLink* dlist_create(void); // 创建链表 void dlist_insert(DLink *link, int pos, void *data); //插入 int dlist_delete(DLink *link,int pos);//删除 void dlist_clean(DLink *link);//清空数据 void dlist_destory(DLink *link);//销毁链表 void print_char(int * dat); //打印一个字符方法 void print_int(int * dat); //打印一个整形的方法 void print(DLink *link, void( *v_print)(int *));//通用的打印方法,用来回调各种类型的数据的方法 void dlist_foreach(DLink *link, void(*visit)(void *ctx, void *dat), void *ctx);//通用的递归方法,用来拓展 #endif
1 //dlink.c 2 #include <stdio.h> 3 #include <malloc.h> 4 #include "dlink.h" 5 6 //初始化头结点 7 static void dlist_init(DLink *link) 8 { 9 link->head = (DListNode*)malloc(sizeof(DListNode)); 10 if(link->head != NULL) 11 { 12 int data='q'; 13 int *dat =&data; 14 link->head->prior = NULL; 15 link->head->next = NULL; 16 17 link->head->dat =dat; 18 } 19 } 20 21 22 //创建一个双向链表 23 DLink* dlist_create(void) 24 { 25 DLink* link ; 26 if(link != NULL) 27 { 28 link->head = NULL; 29 dlist_init(link); 30 } 31 return link; 32 //函数不应该返回一个局部变量,栈内的数据极易丢失 33 //或者函数提供参数进来,该函数提供出事话,这样是可以的 34 } 35 36 37 //新建一个节点 38 static DListNode* dlist_node_create(void* data) 39 { 40 DListNode* node = malloc(sizeof(DListNode)); 41 if(node != NULL) 42 { 43 node->prior = NULL; 44 node->next = NULL; 45 node->dat = data; 46 } 47 return node; 48 } 49 50 //销毁一个节点 51 static void dlist_node_destroy(DListNode* node) 52 { 53 if(node != NULL) 54 { 55 node->next = NULL; 56 node->prior = NULL; 57 free(node); 58 } 59 return; 60 } 61 62 //插入链表 63 void dlist_insert(DLink *link, int pos, void *data) 64 { 65 int i=1; 66 DListNode *p = link->head; 67 DListNode *q = dlist_node_create(data); 68 DListNode *t; //用作记录尾节点 69 while(p) 70 { 71 if(pos==i) 72 { 73 q->next = p->next; 74 if(q->next!=NULL) //当只有头节点的时候插入一个节点会有问题,所以判断一下 75 q->next->prior = q; 76 q->prior = p; 77 p->next = q; 78 } 79 t = p; 80 p = p->next; 81 i++; 82 83 } 84 if(i<pos+1) //当POS很大直接插入到最后面即可 85 { 86 q->prior = t; 87 q->next = t->next; 88 t->next = q; 89 90 } 91 92 } 93 94 void print_int(int * dat) 95 { 96 97 printf("%d ",*dat); 98 99 } 100 101 void print_char(int * dat) 102 { 103 104 printf("%c ",*dat); 105 106 } 107 108 //通用的打印方法,需要传入一个函数,这里可以根据需求打印,就算实际类型不对,我们也可以按照需求打印处类型 109 void print(DLink *link, void( *v_print)(int *)) 110 { 111 DListNode *p =link->head->next; 112 DListNode *q =p ; //一定要注意指针初始化,需要赋初值,否则可能会导致核心段错误,初值是个未知数,并不等于NULL 113 printf("正序: "); 114 while(p) 115 { 116 //我没有返回给主函数的回调函数. 117 v_print(p->dat); 118 q = p; 119 p = p->next; 120 } 121 122 printf("\n逆序: "); 123 while(q!=NULL &&q->prior!=NULL ) 124 { 125 v_print(q->dat); 126 q = q->prior; 127 } 128 printf("\n"); 129 } 130 131 //遍历方法一个函数指针的形参 ,ctx为上下文,这个参数用来返回回调函数中计算的值,所以回调函数会对这个值进行操作 132 void dlist_foreach(DLink *link, void(*visit)(void *ctx, void *dat), void *ctx) 133 { 134 DListNode *p =link->head->next; 135 while(p) 136 { 137 //我就是传说中的回调函数.NB吧. 138 visit(ctx,p->dat); 139 p = p->next; 140 } 141 142 } 143 144 //从链表中得到一个节点 145 static DListNode* dlist_get_node(DLink* link,int index) 146 { 147 DListNode* p = link->head; 148 149 while(p != NULL && p->next != NULL && index > 0) 150 { 151 p = p->next; 152 index--; 153 } 154 if(index>0) 155 { 156 return NULL; 157 } 158 159 return p; 160 } 161 162 //删除一个节点 163 int dlist_delete(DLink *link,int pos) 164 { 165 DListNode *p =dlist_get_node(link,pos); 166 DListNode *q; 167 if(p!=NULL) 168 { 169 q = p->prior; 170 q->next = p->next; 171 q->next->prior = q; 172 dlist_node_destroy(p); //删除节点 173 return 0; 174 } 175 return -1; 176 } 177 178 //清空节点,但保留头结点 179 void dlist_clean(DLink *link) 180 { 181 DListNode *p =link->head->next; 182 DListNode *q; 183 while(p) 184 { 185 q =p->next; 186 dlist_node_destroy(p); 187 p = q; 188 } 189 link->head->next =NULL; 190 } 191 192 //销毁链表 193 void dlist_destory(DLink *link) 194 { 195 dlist_clean(link); 196 dlist_node_destroy(link->head); 197 link->head =NULL; 198 }
#include "dlink.h" //用户自定义函数,试试从不太那个的文件中的调用 void max(int *ctx, int *dat) { if(*dat>*ctx) *ctx = *dat; } //得到值返回到ctx,所以这个值为上下文. void count(void *ctx, void *dat) { int *result = ctx; *result += *((int*)dat); return; }
//测试用的主函数
1 //main.c 2 #include "dlink.h" 3 #include <stdio.h> 4 5 extern void max(void *ctx, void *da); 6 extern void count(void *ctx, void *dat); 7 //经过实验,这里居然只要告诉编译器,我是一个函数就可以!!,形参只是写个程序员看的. 8 9 int main() 10 { 11 12 13 DLink *link=dlist_create(); 14 int c,m; 15 int a[]={5,65,66,67,68,69,70,71,72}; 16 dlist_insert(link, 1, &a[1]); 17 dlist_insert(link, 2, &a[2]); 18 dlist_insert(link, 1, &a[0]); 19 dlist_insert(link, 4, &a[4]); 20 dlist_insert(link, 5, &a[5]); 21 dlist_insert(link, 6, &a[6]); 22 print(link,print_int); 23 dlist_foreach(link, count, &c); 24 dlist_foreach(link, max, &m); 25 printf("max=%d,count=%d\n",m,c); 26 return 0; 27 }