本文是一个单向链表的通用demo,可以用在数据结构的使用
一、链表的特性
二、链表的操作类型
三、通用链表的测试用例
四、使用makefile测试
一、链表的特点
单向链表是利用动态内存分布、使用结构体并配合指针来实现的一种数据结构。相比于数组,单向链表进行数据插入和删除的操作更为简单,但是,调取链表中的元素,只能一个个去访问寻找,不能直接通过位置,一步得到。
二、链表的操作类型
-
创建链表
LinkList* LinkList_Create(); -
销毁链表
void LinkLisk_Destroy(LinkList* list); -
清除链表
void LinkList_Clear(LinkList* list); -
插入节点
int LinkList_Insert(LinkList* list, int pos, LinkListNode* node); -
删除节点
int LinkList_Delete(LinkList* list, int pos); -
获取节点
TLinkListNode* LinkList_Get(LinkList* list, int pos); -
获取链表长度
int LinkList_Length(LinkList* list);
首先我们定义一下LinkList的类型:
typedef void LinkList;
这里将LinkList定义为void是为了通用性,适合任何数据类型。
另外我们还要定义一下节点类型,同样为了通用性,将其定义为void类型:
typedef void TLinkListNode; //这里是返回给调用者用的,所以调用者不需要关注类型,由链表封装实现
而链表真正使用的节点类型定义为:
typedef struct _tagLinkListNode LinkListNode;
struct _tagLinkListNode
{
void* value;
LinkListNode* next;
};
可以看到链表节点由一个void类型的指针和指向下一个节点的指针构成,value用于存储数据,next指向下一个节点的地址
最后定义一下链表的头部,因为一个链表是由头部开始的:
typedef struct _tagheadernode
{
int length;
LinkListNode* header;
}TLinkList;
可以看到链表头部由长度length和指向第一个节点的头部header构成。
接下来我们具体看一下链表每一个操作是如何实现的:
- 创建链表
LinkList* LinkList_Create();
LinkList* LinkList_Create()
{
TLinkList* mlinklist = (TLinkList*)malloc(sizeof(TLinkList));
if(mlinklist != NULL)
{
mlinklist->length = 0;
mlinklist->header = (LinkListNode*)(((int*)&(mlinklist->length))+2);
printf("create link list succeed\n");
}
return mlinklist;
}
首先分配内存,如果分配成功,将长度设置为0,并获取header的值
- 销毁链表
void LinkLisk_Destroy(LinkList* list);
void LinkLisk_Destroy(LinkList* list)
{
TLinkList* mlinklist = (TLinkList*)list;
if(mlinklist != NULL)
{
mlinklist->header = NULL;
mlinklist = NULL;
printf("link list destroy succeed\n");
}
return;
}
销毁链表将header设置成NULL,将list设置成NULL
- 清除链表
void LinkList_Clear(LinkList* list);
void LinkList_Clear(LinkList* list)
{
TLinkList* mlinklist = (TLinkList*)list;
if(mlinklist != NULL)
{
mlinklist->length = 0;
free(mlinklist);
printf("link list clear succeed\n");
}
return;
}
清除链表将length设置成0,并释放内存
- 插入节点
int LinkList_Insert(LinkList* list, int pos, LinkListNode* node);
int LinkList_Insert(LinkList* list, int pos, LinkListNode* node)
{
int insert = 1;
int i = 0;
int ret = -1;
TLinkList* mlinklist = (TLinkList*)list;
insert = insert && (mlinklist != NULL) && (pos <= mlinklist->length) && (node != NULL);
LinkListNode* mnode = (LinkListNode*)malloc(sizeof(LinkListNode));
insert = insert && (mnode != NULL);
printf("insert node addr = %x\n", node);
if(insert)
{
if(mlinklist->length == 0)
{
mnode->value = (LinkListNode*)node;
mnode->next = NULL;
mlinklist->header->next = mnode;
mlinklist->length++;
}
else
{
LinkListNode* current = mlinklist->header;
for(i=0; i<pos && current->next != NULL; i++)
{
current = current->next;
}
mnode->value = (LinkListNode*)node;
mnode->next = current->next;
current->next = mnode;
mlinklist->length++;
}
ret = 0;
}
return ret;
}
插入节点分两种情况,一种是链表长度为0,一种是不为0,为0的时候直接将node传给value,并让头部的next指向该节点,不为0的时候要找到pos的地方,然后将节点插在pos的地方。
- 删除节点
int LinkList_Delete(LinkList* list, int pos);
int LinkList_Delete(LinkList* list, int pos)
{
//bool delete = true;
int i = 0;
int Ret = -1;
TLinkList* mlinklist = (TLinkList*)list;
//delete = delete && (mlinklist != NULL) && (pos <= mlinklist->length) && (mlinklist->length != 0);
LinkListNode* ret = NULL;
if((mlinklist != NULL) && (pos <= mlinklist->length) && (mlinklist->length != 0))
{
LinkListNode* current = mlinklist->header;
for(i=0; i<pos && current->next != NULL; i++)
{
current = current->next;
}
if(mlinklist->length == 1)
{
free(current->next);
current->next = NULL;
mlinklist->length = 0;
}
else
{
ret = current->next;
current->next = ret->next;
free(ret);
ret = NULL;
mlinklist->length--;
}
Ret = 0;
}
return Ret;
}
删除节点同样分为链表长度为1和不为1的情况进行删除
- 获取节点
TLinkListNode* LinkList_Get(LinkList* list, int pos);
TLinkListNode* LinkList_Get(LinkList* list, int pos)
{
int get = 1;
int i = 0;
TLinkList* mlinklist = (TLinkList*)list;
get = get && (mlinklist != NULL) && (pos <= mlinklist->length) && (mlinklist->length != 0);
TLinkListNode* ret = NULL;
if(get)
{
LinkListNode* current = mlinklist->header;
for(i=0; i<pos && current->next != NULL; i++)
{
current = current->next;
}
ret = current->next->value;
printf("get node addr = %x\n", ret);
}
return ret;
}
获取节点首先找到pos对应的节点,然后将value返回
- 获取链表长度
int LinkList_Length(LinkList* list);
int LinkList_Length(LinkList* list)
{
int ret = -1;
TLinkList* mlinklist = (TLinkList*)list;
if(mlinklist != NULL)
ret = mlinklist->length;
return ret;
}
直接返回length
三、通用链表的测试用例
#include<stdio.h>
#include"LinkList.h"
void main()
{
int i = 0;
int array[8] = {1,2,3,4,5,6,7,8};
LinkList* list = LinkList_Create();
for(i=0; i<(sizeof(array)/sizeof(int)); i++)
{
LinkList_Insert(list, LinkList_Length(list), (LinkListNode*)&array[i]);
}
for(i=0; i<(sizeof(array)/sizeof(int)); i++)
{
printf("%d\n", *((int*)LinkList_Get(list, i)));
}
while(LinkList_Length(list) > 0)
{
LinkList_Delete(list, 0);
}
LinkList_Clear(list);
LinkLisk_Destroy(list);
}
测试用例将数组的值每一个插入链表,然后获取出来,然后删除每一个节点,清除链表,最后销毁链表
四、使用makefile测试
CC = gcc
CFLAGS = -g -Wall -O
main:main.o LinkList.o
$(CC) $^ -o $@
%.o:%.c
$(CC) $(CFLAGS) -c $^
clean:
rm -rf main.o LinkList.o main
测试结果:
所有代码和测试用例见:https://download.csdn.net/download/yjlyyj/85520754