栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > C/C++/C#

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

C/C++/C# 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

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

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

目录

1.链表的创建

2.链表的销毁

3.链表的打印

4.链表的插入

 5.链表的删除

6.链表头插

7.链表尾插 

8.链表头删 

9.链表尾删

10.链表查找 

 

 


链表的头文件:

#pragma once

#include
#include
#include
#include

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;
}

 

 

 

 

 

 

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/887382.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号