数据结构初阶之双向循环链表(C语言实现)

将要实现的接口:

//创建结点
LTNode* BuyListNode(LTDateType x);

//初始化
LTNode* ListInit();

//尾插
void ListPushBack(LTNode* phead, LTDateType x);

//尾删
void ListPopBack(LTNode* phead);

//打印
void ListPrint(LTNode* phead);

//头插
void ListPushFront(LTNode* phead, LTDateType x);

//头删
void ListPopFront(LTNode* phead);

//查找
LTNode* ListFind(LTNode* phead, LTDateType x);

//在pos位置插入
void ListInsert(LTNode* pos, LTDateType x);

//删除pos位置
void ListErase(LTNode* pos);

//销毁链表
void ListDestroy(LTNode* phead);

定义结构:
 

typedef int LTDateType;//方便以后类型更改,重定义

typedef struct ListNode
{
	LTDateType val;//存值
	struct ListNode* next;//存下一个结点的地址
	struct ListNode* prev;//存前一个结点的地址
}LTNode;

以下是接口实现代码:

创建结点

//创建结点
LTNode* BuyListNode(LTDateType x)
{
	LTNode* newnode = (LTNode*)malloc(sizeof(LTNode));
	//失败即终止程序
	if (newnode == NULL)
	{
		printf("error\n");
		exit(-1);
	}

	newnode->next = NULL;
	newnode->prev = NULL;
	newnode->val = x;

	return newnode;
}

初始化链表

因为这里将实现的是双向带头循环链表,为了接口的一致性,这里采用返回值使用一级指针而不是使用二级指针(这里需要改变哨兵位的头结点)

//初始化,创建链表的哨兵位的头结点,必须返回,不然没改变实际的哨兵位
LTNode* ListInit()
{
	LTNode* phead = BuyListNode(0);//给予任意值,哨兵位的头结点不存数据

	//一开始next和prev都指向自己
	phead->next = phead;
	phead->prev = phead;

	return phead;
}

打印链表

//打印链表
void ListPrint(LTNode* phead)
{
	assert(phead);//哨兵位不能为空

	//遍历指针,phead是哨兵位,不存储数据
	LTNode* cur = phead->next;

	while (cur != phead)
	{
		printf("%d->", cur->val);
		cur = cur->next;
	}
	printf("NULL\n");
}

在指定位置插入结点

//在pos位置前插入,不用管哨兵位的头结点
void ListInsert(LTNode* pos, LTDateType x)
{
	assert(pos);//待插入位置不能为空

	//创建结点
	LTNode* newnode = BuyListNode(x);

	LTNode* prev = pos->prev;
	
	newnode->prev = prev;
	prev->next = newnode;

	newnode->next = pos;
	pos->prev = newnode;
}

删除指定位置的结点

//删除pos位置
void ListErase(LTNode* pos)
{
	assert(pos);//待删位置不能为空

	LTNode* posprev = pos->prev;
	LTNode* posnext = pos->next;
	
	//将pos前后连接
	posprev->next = posnext;
	posnext->prev = posprev;

	//释放pos
	free(pos);
	//这里置空不置空都不会怎么样,因为这里只是实参的拷贝,最后还是要在外面置空
	pos = NULL;
}

查找值

//查找
LTNode* ListFind(LTNode* phead, LTDateType x)
{
	assert(phead);//链表为空不能查找

	//phead为哨兵位,应该是下一个结点开始查找
	LTNode* cur = phead->next;

	while (cur != phead)
	{
		if (cur->val == x)
		{
			return cur;
		}
		cur = cur->next;
	}

	//找不到
	return NULL;
}

尾插

由于有任意位置插入函数,所以这里既可以调用任意位置插入函数或者再写一个接口

以下的尾删头插头删同理

//尾插,不需要返回,插在哨兵位结点后,传值
void ListPushBack(LTNode* phead, LTDateType x)
{
	//传的哨兵位不能为空
	assert(phead);

	//采用Insert函数插入,phead就是pso
	ListInsert(phead, x);
}
//尾插,不需要返回,插在哨兵位结点后,传值
void ListPushBack(LTNode* phead, LTDateType x)
{
	//传的哨兵位不能为空
	assert(phead);
    //创建结点
	//LTNode* newnode = BuyListNode(x);

	//使用tail遍历
	LTNode* tail = phead->prev;

	//最后一个结点交接
	tail->next = newnode;
	newnode->prev = tail;

	//让最后一个结点与哨兵位结点成循环
	phead->prev = newnode;
	newnode->next = phead;
}

尾删

//尾删
void ListPopBack(LTNode* phead)
{
	assert(phead);//哨兵位不能为空

	//哨兵位不能删
	//assert(phead->next != phead);
	if (phead->next == NULL)
	{
		return;
	}

	//遍历链表
	LTNode* tail = phead->prev;
	LTNode* tailprev = tail->prev;

	//让最后一个结点和前一个结点断开
	tailprev->next = phead;
	//让前一个结点和哨兵位结点成循环
	phead->prev = tailprev;

	//释放
	free(tail);
	//置空 
	tail = NULL;
}
//尾删
void ListPopBack(LTNode* phead)
{
	assert(phead);//哨兵位不能为空

	//使用Erase函数,删除尾结点,不是删除哨兵位
	ListErase(phead->prev);
}

头插

//头插
void ListPushFront(LTNode* phead, LTDateType x)
{
	assert(phead);//哨兵位不能为空

	//创建结点
	LTNode* newnode = BuyListNode(x);

	LTNode* next = phead->next;

	//连接phead和newnode
	phead->next = newnode;
	newnode->prev = phead;

	//让next和newnode连接
	next->prev = newnode;
	newnode->next = next;
}
//头插
void ListPushFront(LTNode* phead, LTDateType x)
{
	assert(phead);//哨兵位不能为空

	//采用Insert函数插入,phead->next才是pos
	ListInsert(phead->next, x);
}

头删

//头删
void ListPopFront(LTNode* phead)
{
	assert(phead);//哨兵位不能为空
	//哨兵位不能删
	if (phead->next == phead)
	{
		return;
	}

	LTNode* next = phead->next;

	phead->next = next->next;
	next->next->prev = phead;

	free(next);
	next = NULL;

}
//头删
void ListPopFront(LTNode* phead)
{
	assert(phead);//哨兵位不能为空

	//使用Erase函数,删除头结点而不是哨兵位
	ListErase(phead->next);

}

查找元素

只能找没有重复的元素,如果是重复的只能找第一个

//查找
LTNode* ListFind(LTNode* phead, LTDateType x)
{
	assert(phead);//链表为空不能查找

	//phead为哨兵位,应该是下一个结点开始查找
	LTNode* cur = phead->next;

	while (cur != phead)
	{
		if (cur->val == x)
		{
			return cur;
		}
		cur = cur->next;
	}

	//找不到
	return NULL;
}

销毁链表

这里需要注意销毁以后,置空还是在调用函数的地方置空,因为这里的是形参,并不会改变实参,不然就会出现野指针

//销毁链表
void ListDestroy(LTNode* phead)
{
	assert(phead);//链表都不存在,不能销毁

	LTNode* cur = phead->next;

	while (cur != phead)
	{
		//在循环里创建方便移动
		LTNode* next = cur->next;
		free(cur);
		cur = next;
	}

	free(cur);
	//这里置空不置空都不会怎么样,因为这里只是实参的拷贝,最后还是要在外面置空
	cur = NULL;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Hiland.

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值