数据结构-带头双向循环链表的实现

这篇具有很好参考价值的文章主要介绍了数据结构-带头双向循环链表的实现。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

前言 

        带头双向循环链表是一种重要的数据结构,它的结构是很完美的,它弥补了单链表的许多不足,让我们一起来了解一下它是如何实现的吧!

1.节点的结构

        它的节点中存储着数据和两个指针,一个指针_prev用来记录前一个节点的地址,另一个指针_next 用来记录后一个节点的地址。

数据结构-带头双向循环链表的实现,数据结构,链表

typedef int LTDataType;
typedef struct ListNode
{
	struct ListNode* _next;
	struct ListNode* _prev;
	LTDataType _data;
}ListNode;

2.尾部的插入和删除的实现

        由于这是带头循环的双向链表,所以尾插只需要在它的头结点的_prev 处进行插入,然后重新链接就可以了。如图:数据结构-带头双向循环链表的实现,数据结构,链表 

        如果只有一个头结点,插入也是一样的。

void ListPushBack(ListNode* phead, LTDataType data)//尾插
{
	assert(phead);
	ListNode* newNode = BuyListNode(data);//申请新节点

	ListNode* tail = phead->_prev;
	//找尾结点
	//链接新节点和尾结点
	tail->_next = newNode;
	newNode->_prev = tail;
	//与头结点进行链接
	phead->_prev = newNode;
	newNode->_next = phead;
}

         尾部的删除,首先需要找到链表的尾和尾的前一个节点,删除尾结点之后,将前一个节点进与头结点进行链接,如图:

数据结构-带头双向循环链表的实现,数据结构,链表

void ListPopBack(ListNode* phead)//尾删除
{
	//确保指针不为空
	assert(phead);
	assert(phead->_next != phead);//保留头结点
	//找尾
	ListNode* tail = phead->_prev;
	ListNode* newTail = tail->_prev;//找到新的尾结点
	//删除旧的尾结点
	free(tail);
	//重新链接头尾节点
	newTail->_next = phead;
	phead->_prev = newTail;
}

3.头部插入和删除的实现

        头部的插入,删除和尾部的插入,删除类似,需要注意的是删除的不是 头结点,是存储有效数据的第一个节点,插入数据也是在第一个有效数据节点和头结点之间插入。如图:

数据结构-带头双向循环链表的实现,数据结构,链表

数据结构-带头双向循环链表的实现,数据结构,链表 

void ListPushFront(ListNode* phead, LTDataType data)//头插
{
	//确保指针不为空
	assert(phead);
	//申请新的节点
	ListNode* newNode = BuyListNode(data);
	//进行链接
	ListNode* realHead = phead->_next;
	realHead->_prev = newNode;
	newNode->_next = realHead;
	phead->_next = newNode;
	newNode->_prev = phead;
}
void ListPopFront(ListNode* phead)//头删插
{
	//指针存在
	assert(phead);
	//并且确保不能删除头结点
	assert(phead->_next != phead);
	//找到真正的头
	ListNode* realHead = phead->_next;
	ListNode* realHeadNext = realHead->_next;
	//删除头节点
	free(realHead);
	//重新链接
	phead->_next = realHeadNext;
	realHeadNext->_prev = phead;
}

4.任意位置的插入和删除 

        在任意位置进行插入和删除,需要知道节点的指针,插入或者删除节点之后需要 更新节点的链接关系。如图:数据结构-带头双向循环链表的实现,数据结构,链表

数据结构-带头双向循环链表的实现,数据结构,链表 

void ListInsert(ListNode* pos, LTDataType data)//pos位置之前的插入
{
	assert(pos);//确保指针有效
	ListNode* newNode = BuyListNode(data);//申请节点
	//进行链接
	ListNode* prev = pos->_prev;
	ListNode* next = pos;

	prev->_next = newNode;
	newNode->_prev = prev;

	newNode->_next = next;
	next->_prev = newNode;
}
void ListErase(ListNode* pos)//pos位置的删除
{
	//确保指针有效
	assert(pos);
	ListNode* next = pos->_next;
	ListNode* prev = pos->_prev;
	//删除pos所指向的节点
	free(pos);
	//进行重新链接
	prev->_next = next;
	next->_prev = prev;
}

         对任意位置的插入和删除进行测试时,可以通过复用接口来实现,头插尾插,头删尾删都可以调用这两个接口来实现,如下:

void ListPushBack(ListNode* phead, LTDataType data)//尾插
{
	ListInsert(phead, data);
}
void ListPopBack(ListNode* phead)//尾删除
{
	ListErase(phead->_prev);
}
void ListPushFront(ListNode* phead, LTDataType data)//头插
{
	ListInsert(phead->_next,data);
}
void ListPopFront(ListNode* phead)//头删插
{
	ListErase(phead->_next);
}

5.链表的初始化和删除

        带头的双向循环链表初始化的时候就需要申请内存给头节点。 

ListNode* BuyListNode(LTDataType data)//申请节点
{
	ListNode* newNode = (ListNode*)malloc(sizeof(ListNode));
	if (newNode == NULL)
	{
		printf("申请空间失败\n");
		exit(-1);
	}
	newNode->_data = data;
	return newNode;
}
void ListInit(ListNode** pphead)//初始化链表
{
	*pphead = BuyListNode(0);
	//申请头结点
	//并且初始化
	(*pphead)->_next = *pphead;
	(*pphead)->_prev = *pphead;
}
//清理链表
void ListClear(ListNode* phead)
{
	assert(phead);//确保链表不为空
	assert(phead->_next != phead);//除了确保不清理头结点
	ListNode* cur = phead->_next;

	while (cur != phead)
	{
		ListNode* clearNode = cur;
		cur = cur->_next;
		free(clearNode);
	}
}
void ListDestory(ListNode** ppHead)//摧毁链表
{
	assert(*ppHead);//确保指针不为空
	ListClear(*ppHead);
	free(*ppHead);//释放头结点
}

6.查找并修改链表的节点的数据

        查找和修改是一起的,实现查找就可以实现 修改链表的值。

ListNode* ListFind(ListNode* phead, LTDataType data)//查找链表
{
	assert(phead);
	ListNode* cur = phead->_next;
	while (cur != phead)
	{
		if (cur->_data == data)
			return cur;
		cur = cur->_next;
	}
	return NULL;//找不到返回NULL
}
void ListTest4()
{
	//查找和修改的测试
	ListNode* pHead = NULL;
	ListInit(&pHead);
	ListPushFront(pHead, 1);
	ListPushFront(pHead, 2);
	ListPushFront(pHead, 3);
	ListPushFront(pHead, 4);
	ListPushFront(pHead, 5);
	ListPushFront(pHead, 6);

	ListNode* node = ListFind(pHead, 3);//在链表中查找
	if (node)
	{
		//修改链表的值
		node->_data = 90;
	}
	ListPrint(pHead);
	ListDestory(&pHead);
}

7.全部代码

//List.h

#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
typedef int LTDataType;
typedef struct ListNode
{
	struct ListNode* _next;
	struct ListNode* _prev;
	LTDataType _data;
}ListNode;

void ListPushBack(ListNode* phead, LTDataType data);//尾插

void ListPopBack(ListNode* phead);//尾删除
void ListPushFront(ListNode* phead,LTDataType data);//头插
void ListPopFront(ListNode* phead);//头删插
ListNode* BuyListNode(LTDataType data);//申请节点
void ListInit(ListNode** pphead);//初始化链表

void ListInsert(ListNode* pos, LTDataType data);//pos位置之前的插入

void ListErase(ListNode* pos);//pos位置的删除
//清理链表
void ListClear(ListNode* phead);

void ListDestory(ListNode** ppHead);//摧毁链表

void ListPrint(ListNode* phead);//打印链表
ListNode* ListFind(ListNode* phead, LTDataType data);//查找链表

 //List.c

#include"List.h"
void ListPushBack(ListNode* phead, LTDataType data)//尾插
{
	assert(phead);
	ListNode* newNode = BuyListNode(data);//申请新节点

	ListNode* tail = phead->_prev;
	//找尾结点
	//链接新节点和尾结点
	tail->_next = newNode;
	newNode->_prev = tail;
	//与头结点进行链接
	phead->_prev = newNode;
	newNode->_next = phead;
}
void ListPopBack(ListNode* phead)//尾删除
{
	//确保指针不为空
	assert(phead);
	assert(phead->_next != phead);//保留头结点
	//找尾
	ListNode* tail = phead->_prev;
	ListNode* newTail = tail->_prev;//找到新的尾结点
	//删除旧的尾结点
	free(tail);
	//重新链接头尾节点
	newTail->_next = phead;
	phead->_prev = newTail;
}
void ListPushFront(ListNode* phead, LTDataType data)//头插
{
	//确保指针不为空
	assert(phead);
	//申请新的节点
	ListNode* newNode = BuyListNode(data);
	//进行链接
	ListNode* realHead = phead->_next;
	realHead->_prev = newNode;
	newNode->_next = realHead;
	phead->_next = newNode;
	newNode->_prev = phead;
}
void ListPopFront(ListNode* phead)//头删插
{
	//指针存在
	assert(phead);
	//并且确保不能删除头结点
	assert(phead->_next != phead);
	//找到真正的头
	ListNode* realHead = phead->_next;
	ListNode* realHeadNext = realHead->_next;
	//删除头节点
	free(realHead);
	//重新链接
	phead->_next = realHeadNext;
	realHeadNext->_prev = phead;
}
ListNode* BuyListNode(LTDataType data)//申请节点
{
	ListNode* newNode = (ListNode*)malloc(sizeof(ListNode));
	if (newNode == NULL)
	{
		printf("申请空间失败\n");
		exit(-1);
	}
	newNode->_data = data;
	return newNode;
}
void ListInit(ListNode** pphead)//初始化链表
{
	*pphead = BuyListNode(0);
	//申请头结点
	//并且初始化
	(*pphead)->_next = *pphead;
	(*pphead)->_prev = *pphead;
}
void ListPrint(ListNode* phead)//打印链表
{
	assert(phead);
	ListNode* cur = phead->_next;
	while (cur != phead)
	{
		printf("%d ", cur->_data);
		cur = cur->_next;
	}
	printf("\n");
}
void ListInsert(ListNode* pos, LTDataType data)//pos位置之前的插入
{
	assert(pos);//确保指针有效
	ListNode* newNode = BuyListNode(data);//申请节点
	//进行链接
	ListNode* prev = pos->_prev;
	ListNode* next = pos;

	prev->_next = newNode;
	newNode->_prev = prev;

	newNode->_next = next;
	next->_prev = newNode;
}
void ListErase(ListNode* pos)//pos位置的删除
{
	//确保指针有效
	assert(pos);
	ListNode* next = pos->_next;
	ListNode* prev = pos->_prev;
	//删除pos所指向的节点
	free(pos);
	//进行重新链接
	prev->_next = next;
	next->_prev = prev;
}
//清理链表
void ListClear(ListNode* phead)
{
	assert(phead);//确保链表不为空
	assert(phead->_next != phead);//除了确保不清理头结点
	ListNode* cur = phead->_next;

	while (cur != phead)
	{
		ListNode* clearNode = cur;
		cur = cur->_next;
		free(clearNode);
	}
}
void ListDestory(ListNode** ppHead)//摧毁链表
{
	assert(*ppHead);//确保指针不为空
	ListClear(*ppHead);
	free(*ppHead);//释放头结点
}
ListNode* ListFind(ListNode* phead, LTDataType data)//查找链表
{
	assert(phead);
	ListNode* cur = phead->_next;
	while (cur != phead)
	{
		if (cur->_data == data)
			return cur;
		cur = cur->_next;
	}
	return NULL;//找不到返回NULL
}

//test.c

#include"List.h"
void ListTest1()
{
	//尾插尾删的测试代码
	ListNode* pHead = NULL;
	ListInit(&pHead);
	ListPushBack(pHead, 1);
	ListPushBack(pHead, 2);
	ListPushBack(pHead, 3);
	ListPushBack(pHead, 4);
	ListPushBack(pHead, 5);
	ListPushBack(pHead, 6);
	ListPrint(pHead);
	ListPopBack(pHead);
	ListPopBack(pHead);
	ListPopBack(pHead);
	ListPopBack(pHead);
	ListPopBack(pHead);
	ListPopBack(pHead);
	//ListPopBack(pHead);

}
void ListTest2()
{
	//头插头删的测试
	ListNode* pHead = NULL;
	ListInit(&pHead);
	ListPushFront(pHead, 1);
	ListPushFront(pHead, 2);
	ListPushFront(pHead, 3);
	ListPushFront(pHead, 4);
	ListPushFront(pHead, 5);
	ListPushFront(pHead, 6);
	ListPrint(pHead);
	ListPopFront(pHead);
	ListPopFront(pHead);
	ListPopFront(pHead);
	ListPopFront(pHead);
	ListPopFront(pHead);
	ListPopFront(pHead);
	ListPopFront(pHead);

}
void ListTest3()
{
	//Destory和Clear的测试
	ListNode* pHead = NULL;
	ListInit(&pHead);
	ListPushFront(pHead, 1);
	ListPushFront(pHead, 2);
	ListPushFront(pHead, 3);
	ListPushFront(pHead, 4);
	ListPushFront(pHead, 5);
	ListPushFront(pHead, 6);
	ListDestory(&pHead);
}
int main()
{
	ListTest3();
	return 0;
}

 

8.测试代码

void ListTest1()
{
	//尾插尾删的测试代码
	ListNode* pHead = NULL;
	ListInit(&pHead);
	ListPushBack(pHead, 1);
	ListPushBack(pHead, 2);
	ListPushBack(pHead, 3);
	ListPushBack(pHead, 4);
	ListPushBack(pHead, 5);
	ListPushBack(pHead, 6);
	ListPrint(pHead);
	ListPopBack(pHead);
	ListPopBack(pHead);
	ListPopBack(pHead);
	ListPopBack(pHead);
	ListPopBack(pHead);
	ListPopBack(pHead);
	//ListPopBack(pHead);

}
void ListTest2()
{
	//头插头删的测试
	ListNode* pHead = NULL;
	ListInit(&pHead);
	ListPushFront(pHead, 1);
	ListPushFront(pHead, 2);
	ListPushFront(pHead, 3);
	ListPushFront(pHead, 4);
	ListPushFront(pHead, 5);
	ListPushFront(pHead, 6);
	ListPrint(pHead);
	ListPopFront(pHead);
	ListPopFront(pHead);
	ListPopFront(pHead);
	ListPopFront(pHead);
	ListPopFront(pHead);
	ListPopFront(pHead);
	ListPopFront(pHead);

}
void ListTest3()
{
	//Destory和Clear的测试
	ListNode* pHead = NULL;
	ListInit(&pHead);
	ListPushFront(pHead, 1);
	ListPushFront(pHead, 2);
	ListPushFront(pHead, 3);
	ListPushFront(pHead, 4);
	ListPushFront(pHead, 5);
	ListPushFront(pHead, 6);
	ListDestory(&pHead);
}

 文章来源地址https://www.toymoban.com/news/detail-644653.html

到了这里,关于数据结构-带头双向循环链表的实现的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 【数据结构】链表:带头双向循环链表的增删查改

    本篇要分享的内容是带头双向链表,以下为本片目录 目录 一、链表的所有结构 二、带头双向链表 2.1尾部插入 2.2哨兵位的初始化 2.3头部插入 2.4 打印链表 2.5尾部删除 2.6头部删除  2.7查找结点 2.8任意位置插入 2.9任意位置删除  在刚开始接触链表的时候,我们所学仅仅所学的

    2024年02月05日
    浏览(91)
  • 【数据结构】实现带头双向循环链表

    之前我们已经学习了单链表,有了单链表的基础,现在开始学习带头双向循环链表~ 结构最复杂 ,一般用在单独存储数据。 实际中使用的链表数据结构,都是带头双向循环链表 。另外这个结构虽然结构复杂,但是使用代码实现以后会发现 结构会带来很多优势 ,实现反而简单

    2024年02月10日
    浏览(45)
  • 【数据结构】带头双向循环链表及其实现

    目录 1.带头双向循环链表 2.带头双向循环链表实现 2.1初始化 2.2销毁 2.3头插 2.4链表打印 2.5头删数据 2.6尾插数据 2.7尾删数据 2.8链表判空  2.9查找一个数据 2.10在pos位置前插入数据 2.11删除pos位置 2.12求链表的长度 2.顺序表和链表的比较 我们已经实现了无头单向循环链表 带头双

    2024年02月10日
    浏览(45)
  • 数据结构: 线性表(带头双向循环链表实现)

    之前一章学习了单链表的相关操作, 但是单链表的限制却很多, 比如不能倒序扫描链表, 解决方法是在数据结构上附加一个域, 使它包含指向前一个单元的指针即可. 那么怎么定义数据结构呢? 首先我们先了解以下链表的分类 链表的结构非常多样, 以下情况组合起来就有 8 中链表

    2024年02月14日
    浏览(41)
  • 数据结构之双向带头循环链表函数功能实现与详细解析

    个人主页:点我进入主页 专栏分类:C语言初阶      C语言程序设计————KTV       C语言小游戏     C语言进阶 C语言刷题       数据结构初阶 欢迎大家点赞,评论,收藏。 一起努力,一起奔赴大厂。 目录 1.前言 2.带头双向循环链表函数实现 3.总结         在前面我

    2024年02月05日
    浏览(54)
  • 【数据结构和算法】实现带头双向循环链表(最复杂的链表)

    前文,我们实现了认识了链表这一结构,并实现了无头单向非循环链表,接下来我们实现另一种常用的链表结构,带头双向循环链表。如有仍不了解单向链表的,请看这一篇文章(7条消息) 【数据结构和算法】认识线性表中的链表,并实现单向链表_小王学代码的博客-CSDN博客

    2024年01月17日
    浏览(78)
  • 数据结构入门(C语言版)线性表带头双向循环链表接口实现

    在上一篇博客我们讲述了链表的概念和结构,还实现了无头单向非循环链表接口写法,那么这一章节,我们来实现另一种常用的链表组成结构——带头双向循环链表。 如果对前面的链表基本概念还是不了解,可以看作者的上一篇博客: 线性表中链表介绍及无头单向非循环链

    2023年04月12日
    浏览(47)
  • 追梦之旅【数据结构篇】——详解C语言动态实现带头结点的双向循环链表结构

        😎博客昵称:博客小梦 😊最喜欢的座右铭:全神贯注的上吧!!! 😊作者简介:一名热爱C/C++,算法等技术、喜爱运动、热爱K歌、敢于追梦的小博主! 😘博主小留言:哈喽! 😄各位CSDN的uu们,我是你的博客好友小梦,希望我的文章可以给您带来一定的帮助,话不

    2024年01月17日
    浏览(61)
  • 链接未来:深入理解链表数据结构(二.c语言实现带头双向循环链表)

    上篇文章简述讲解了链表的基本概念并且实现了无头单向不循环链表:链接未来:深入理解链表数据结构(一.c语言实现无头单向非循环链表)-CSDN博客 那今天接着给大家带来带头双向循环链表的实现 : 头文件DoubleList.h:用来基础准备(常量定义,typedef),链表表的基本框架

    2024年01月16日
    浏览(60)
  • 带头双向循环链表--数据结构

    😶‍🌫️😶‍🌫️😶‍🌫️😶‍🌫️Take your time ! 😶‍🌫️😶‍🌫️😶‍🌫️😶‍🌫️ 💥个人主页:🔥🔥🔥大魔王🔥🔥🔥 💥所属专栏:🔥魔王的修炼之路–数据结构🔥 如果你觉得这篇文章对你有帮助,请在文章结尾处留下你的 点赞 👍和 关注 💖,支持一

    2024年02月01日
    浏览(55)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包