数据结构初阶--双向带头循环链表的实现(C语言)

链表是一种物理存储结构上非连续,非顺序的存储结构,数据元素的逻辑顺序通过链表中的指针链接实现。在链表的所有结构中,双向带头循环链表是链表中存储数据的最优结构。在以下代码中,实现了链表创建,销毁,打印,头插,尾插,头删,尾删,插入,删除,查找的接口。通过节点中的前驱指针和后驱指针,使数据的访问比单链表更加方便,在代码实现上也更加简洁。

目录

1.链表的创建

2.链表的销毁

3.链表的打印

4.链表的插入

 5.链表的删除

6.链表头插

7.链表尾插 

8.链表头删 

9.链表尾删

10.链表查找 

 

 


链表的头文件:

#pragma once

#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
#include<stdbool.h>

typedef int LTDatatype;
typedef struct Listnode
{
	LTDatatype data;
	struct Listnode* next;
	struct Listnode* prev;
}Listnode;

Listnode* ListCreat();
void ListDestory(Listnode* phead);
void ListPrint(Listnode* phead);

void ListPushFont(Listnode* phead, LTDatatype x);
void ListPushBack(Listnode* phead, LTDatatype x);
void ListPopFront(Listnode* phead);
void ListPopBack(Listnode* phead);

void ListInsert(Listnode* pos, LTDatatype x);
void ListErase(Listnode* pos);

Listnode* ListFind(Listnode* phead, LTDatatype x);

1.链表的创建

创建一个哨兵位的头节点,该节点不存储有效数据,前驱指针和后驱指针均指向自己,由于增加节点在很多接口中都会用到,增加节点被封装成一个函数。

Listnode* BuyListNode(LTDatatype x)
{
	Listnode* newnode = (Listnode*)malloc(sizeof(Listnode));
	if (newnode == NULL)
	{
		perror("malloc fail");
		exit(-1);
	}
	newnode->data = x;
	newnode->prev = NULL;
	newnode->prev = NULL;
}
Listnode* ListCreat()
{
	Listnode* phead = BuyListNode(-1);
	phead->next = phead;
	phead->prev = phead;
	return phead;
}

2.链表的销毁

从头遍历链表并释放每一个节点,最后释放头节点

void ListDestory(Listnode* phead)
{
	assert(phead);
	assert(phead->next);
	Listnode* cur = phead->next;
	while (cur != phead)
	{
		Listnode* next = cur->next;
		cur->next = NULL;
		cur->prev = NULL;
		free(cur);
		cur = next;
	}
	free(phead);
    phead = NULL;
}

3.链表的打印

从哨兵位的下一个节点开始遍历并打印,直到回到哨兵位

void ListPrint(Listnode* phead)
{
	assert(phead);
	Listnode* cur = phead->next;
	while (cur != phead)
	{
		printf("%d ", cur->data);
		cur = cur->next;
	}
	printf("\n");
}

4.链表的插入

默认在指定位置的前一个位置插入,注意在更改指针指向时,先记录前一个节点的位置。如果不想记录,也可以选择最后更改pos的前驱指针的指向。

void ListInsert(Listnode* pos, LTDatatype x)
{
	assert(pos);
	Listnode* prev = pos->prev;
	Listnode* newnode = BuyListNode(x);
	prev->next = newnode;
	newnode->prev = prev;
	newnode->next = pos;
	pos->prev = newnode;
}

 5.链表的删除

记录指定位置的节点的前后节点,释放指定位置的节点,更改前后两个节点的指针指向

void ListErase(Listnode* pos)
{
	assert(pos);
	Listnode* prev = pos->prev;
	Listnode* next = pos->next;
	prev->next = next;
	next->prev = prev;
	free(pos);
}

6.链表头插

在实现了插入接口的前提下,可以对插入接口进行复用,哨兵位的下一个节点就是有效数据的头节点。

void ListPushFont(Listnode* phead, LTDatatype x)
{
	assert(phead);
	ListInsert(phead->next, x);
}

7.链表尾插 

对插入接口复用,哨兵位的节点的前驱指针指向链表的尾节点。插入接口默认插在指定节点的前一个位置,因此对插入接口传入哨兵位节点的地址。

void ListPushBack(Listnode* phead, LTDatatype x)
{
	assert(phead);
	ListInsert(phead, x);
}

8.链表头删 

对删除接口进行复用

void ListPopFront(Listnode* phead)
{
	assert(phead);
	assert(phead->next);
	ListErase(phead->next);
}

9.链表尾删

void ListPopBack(Listnode* phead)
{
	assert(phead);
	ListErase(phead->prev);
}

10.链表查找 

遍历寻找与参数值拥有相同值的节点

Listnode* ListFind(Listnode* phead, LTDatatype x)
{
	assert(phead);
	assert(phead->next);
	Listnode* cur = phead->next;
	while (cur != phead)
	{
		if (x == cur->data)
		{
			return cur;
		}
		cur = cur->next;
	}
	return NULL;
}

 

 

 

 

 

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值