1. 程式人生 > >一個通用連結串列的簡單實現

一個通用連結串列的簡單實現

最近在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