一个通用链表的简单实现

最近在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();

//销毁一个链表头节点 以及链表内所有元素 返回NULL
ptrLinkList 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;
	else	s->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;

	}
	else
		p->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.o
	gcc test.o links.o -o test
test.o:test.c links.h
	gcc -c -g test.c 
links.o:links.c links.h
	gcc -c -g links.c 
#this is a makefile


  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值