一个通用链表的简单实现

来源:互联网 发布:数据治理成熟度模型 编辑:程序博客网 时间:2024/05/21 17:52

最近在CSDN上看到了absurd大神的几篇关于系统程序员成长计划的的博文

里面提到了关于通用链表实现的思想,虽然数据结构学的还行,但是真的没写过通用的链表,对封装的认识比较浅显!

于是乎决定实现一下,真正开始写才发现,对我这么个眼高手低的菜鸟来说挺有难度的。写篇博文记录下。


整体实现的主题思想还是absurd大神博文中的两个重要思想:

1, 链表数据段存指针void*

2 采用回调函数来使的链表的基本函数更有通用性

贴代码:

links.h

#ifndef LINKS_H_#define LINKS_H_#include <malloc.h>#include <stdbool.h>#include <stdio.h>#define LEN_LIST sizeof(DListNode)typedef bool (*DListDataPrintFunc)(void* data);   //打印链表的回调函数接口typedef bool (*DListVistFunc)(void* data, void* ctx); //遍利链表的回掉函数接口typedef bool (*DListDataAssign)(void* data, void* ctx);typedef struct _DListNode{struct _DListNode* prev;struct _DListNode* next;void * data;//int data;}DListNode,*ptrDListNode;typedef struct {ptrDListNode head,tail;int len;}LinkList,*ptrLinkList;//开辟一个节点 返回指向该节点的指针ptrDListNode CreateNode();//释放一个节点内存 返回空指针值ptrDListNode DestroyDListNode(ptrDListNode e);//初始化一个链表头节点ptrLinkList InitList();//销毁一个链表头节点 以及链表内所有元素 返回NULLptrLinkList DestroyLinkList(ptrLinkList L);//为链表的节点赋值 bool DListNodeGetData(void* data, DListDataAssign assign, void* ctx);//在第n个节点后插入s指向的节点bool InsDListNode(ptrLinkList L, ptrDListNode s, int n);//删除第n个节点 用q返回bool DelDListNode(ptrLinkList L, ptrDListNode* q, int n);//打印链表void links_print(ptrLinkList L, DListDataPrintFunc);//遍历链表 根据回调函数 用ctx返回所求结果bool Dlist_Foreach(ptrLinkList L, DListVistFunc visit, void* ctx);#endif//  
links.c

#include "links.h"//给节点data赋值bool DListNodeGetData(void* data, DListDataAssign assign, void* ctx){assign(data,ctx);}//开辟一个结点ptrDListNode CreateNode(int size_data){ptrDListNode p = (ptrDListNode) malloc(LEN_LIST);p->prev = NULL;p->next = NULL;p->data = (void*) malloc(size_data);return p;}//初始化头节点ptrLinkList InitList(){ptrLinkList L = (ptrLinkList) malloc(sizeof(LinkList));L->head = NULL;L->tail = NULL;L->len = 0;return L;}//销毁一个节点ptrDListNode DestroyDListNode(ptrDListNode e){free(e->data);free(e);e = NULL;return NULL;}//销毁整个链表ptrLinkList DestroyLinkList(ptrLinkList L){ptrDListNode p = L->head;int n = L->len;while(n--){p = p->next;DestroyDListNode(L->head);L->head = p;}free(L);return NULL;}//在第一个节点前插入static void InsFirst(ptrLinkList L, ptrDListNode s){ptrDListNode p = L->head;L->head = s;s->prev = NULL;if(L->len == 0){L->tail = s;s->next = NULL;}else{s->next = p;p->prev = s;}L->len++;}//在第n个节点后插入bool InsDListNode(ptrLinkList L, ptrDListNode s, int n){if(n > L->len || n < 0 ) return false;if(!n){   InsFirst(L,s);return true;}ptrDListNode p = L->head;if(n == L->len) L->tail = s; while(--n){p = p->next;}s->next = p->next;p->next = s;s->prev = p;if(L->tail != s)s->next->prev = s;elses->next = NULL;L->len ++;return true;}//删除第一个节点q返回static bool DelFirst(ptrLinkList L, ptrDListNode* q){if(L->len == 0) return false;*q = L->head;if(L->len == 1){  L->head = NULL;  L->tail = NULL;} else{L->head = L->head->next;L->head->prev = NULL;}(*q)->next = NULL;L->len--;return true;}//删除第n个节点 q你返回bool DelDListNode(ptrLinkList L, ptrDListNode* q, int n){if(n > L->len || n < 0) return false;if(!n){DelFirst(L,q);return true;}ptrDListNode p = L->head;while(--n){p = p->next;}if(p == L->tail){L->tail = p->prev;}elsep->next->prev = p->prev;p->prev->next = p->next;*q = p;(*q)->next = NULL;(*q)->prev = NULL;L->len--;return true;}//遍历访问bool Dlist_Foreach(ptrLinkList L, DListVistFunc visit, void* ctx){ptrDListNode p = L->head;int n = L->len;long long* ctx_temp = ctx;*ctx_temp = 0;while(n--){visit(p->data, ctx);p = p->next;}return true;}//遍历打印void links_print(ptrLinkList L, DListDataPrintFunc print){ptrDListNode p = L->head;int n = L->len;while(n--){print(p->data);putchar('\n');p = p->next;}}
test.c

#include <stdio.h>#include "links.h"#define DATA_SIZE sizeof(Node)typedef struct Node{int a;int b;}Node,*ptrNode;static bool Data_Assign(void* data, ptrNode ctx){ptrNode temp  = data;temp->a = ctx->a;temp->b = ctx->b;return true;}static bool print_int(void* data){ptrNode temp = data;printf("%d %d",temp->a, temp->b);return true;}static bool get_sum(void* data, void* ctx){ptrNode temp = data;long long* result = ctx;*result += temp->b;return true;}static bool find_max(void* data, void* ctx){ptrNode temp = data;int* pimax = ctx;if(*pimax < temp->a) *pimax = temp->a;return true;}int main(){int i;ptrLinkList p = InitList();ptrDListNode s = CreateNode(DATA_SIZE);ptrDListNode s1 = CreateNode(DATA_SIZE);ptrDListNode s2 = CreateNode(DATA_SIZE);ptrDListNode s3 = CreateNode(DATA_SIZE);ptrDListNode s4;long long sum;int imax;Node ss[5];for(i = 1; i<5; i++){ss[i].a = i;ss[i].b = i*10 + i;}DListNodeGetData(s->data, Data_Assign, &ss[1]);DListNodeGetData(s1->data, Data_Assign, &ss[2]);DListNodeGetData(s2->data, Data_Assign, &ss[3]);DListNodeGetData(s3->data, Data_Assign, &ss[4]);InsDListNode(p,s,0);InsDListNode(p,s1,0);InsDListNode(p,s2,0);InsDListNode(p,s3,3);links_print(p,print_int);Dlist_Foreach(p, get_sum, &sum);Dlist_Foreach(p, find_max, &imax);printf("%lld %d\n",sum,imax);DestroyLinkList(p);return 0;}


因为第一次写,首先写了一个固定int型数据的链表,实现了基本的插入删除操作。为了通用性开始使用void*来存数据段,因为指针一块儿仍旧是眼高手低基础不牢,先假设是非结构体数据进行测试,编写了几个常规的回调函数,实现遍历打印,遍历求和,遍历求最大值。

毕竟绝大多数的链表数据段都是存储的结构体,真正存储结构体的时候又涉及到void型指针进行内存分配的问题,这里依旧需要用回调函数来完成,毕竟内部函数不知道到底是怎样的一个结构体。

整体代码测试通过,由于在linux下编写的,gdb调试用的不熟练,效率太低,而且使用makefile之后我都不会用gdb调试了,list都不出代码了,脑袋完全大了。这次也是人生第一次编写makefile文件。贴出来吧,以后回来改,估计是makefile写的有问题才整的gdb调试的时候只能run不能list....

makefile

test:test.o links.ogcc test.o links.o -o testtest.o:test.c links.hgcc -c -g test.c links.o:links.c links.hgcc -c -g links.c #this is a makefile