双向带头循环链表的实现

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

1.学习第一步:

当我们要学习和了解一个事物时,我们要做的第一步便是对这个事物有一个体的的了解。现在我们要学习双向带头循环链表的第一步也是这个,我们现在先来了解一下这种链表的结构----双向带头循环链表的实现

就像该图所呈现的那样,双向循环链表就是长这样。但是你可千万不要将head当成head。 这里的head是一个哨兵位的头!

二,实现带头双向循环链表

 在上面的介绍中,相信在你的脑海中已经有了带头双向循环链表的样子了。现在我们就来实现一下吧。 

2.1:链表的诞生(链表初始化) 

链表的初始化的目的就是要将带头双向循环链表的头(head)给搞出来。当然,在此之前还是要有一个链表结点的结构。

结构:

typedef int datatype;
 typedef struct ListNode{
	 datatype data;
	 struct ListNode* next;
	 struct ListNode* prev;

}ListNode;

第一个功能的实现——初始化链表:InitList()

void InitList(ListNode* head)
{
	head->next = head;
	head->prev = head;
	head->data = -1;
}
当然,在初始化操作之前还得先创建一个节点传进InitList()中。这一步操作是在test.c文件中的。
void test1()
{
	ListNode* list =(ListNode*) malloc(sizeof(ListNode));//创建节点
	InitList(list);

}
int main()
{
	test1();
	return 0;
}

 第二个功能的实现——尾插:pushBack()

相对于单链表来说,简直小菜一碟!

void pushBack(ListNode* head,datatype x)

{
    assert(head);
	ListNode* newnode = Buynewnode(x);
	ListNode* tail = head->prev;

	tail->next = newnode;
	newnode->prev = tail;

	newnode->next = head;
	head->prev = newnode;

}

第三个功能的实现——ListPrint(Listnode* head)

这个打印函数实现起来那就更加得心应手了,只不够这个函数的终止条件是比较特殊的。它的终止条件是当指针走到哨兵位的头结点时就停止。当然这个指针的起始位置是哨兵位的下一位!

void Listprint(ListNode* head)
{
    assert(head);
	ListNode* cur = head->next;
	while (cur != head)
	{
		printf("%d<=>", cur->data);
		cur = cur->next;
	}
	printf("NULL\n");
	
}

 第四个功能的实现——头插

头插的意思就是生成一个新的节点然后添加到哨兵位的头节点的后面变成新的头节点。

void pushFront(ListNode* head, datatype x)
{
    assert(head);
	ListNode* newnode = Buynewnode(x);
	ListNode* next = head->next;

	head->next = newnode;
	newnode->prev = head;

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

第八个功能——中间插入

 这个函数是个能将头插,尾插两种功能联合起来实现的函数(改一下传入的参数就行了)。

先找到指定的节点:

ListNode* ListFind(ListNode* pHead, LTDataType x)
{
	assert(pHead);
	ListNode* cur = pHead;
	while (cur->next != pHead)
	{
		if (cur->val == x)
		{
			return cur;
		}
		cur = cur->next;
	}
	if (cur->val == x)
	{
		return cur;
	}
	return NULL;
}

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

在指定元素的后面插入:

void ListInsert(ListNode* pos, LTDataType x)//中间插入,在pos的后面插入一个节点
{
	assert(pos);
	ListNode* newnode = Buynewnode(x);
	ListNode* next = pos->next;

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

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

 第五个功能的实现——尾删

尾删尾删就是要把链表尾部的节点给删除掉,在这个带头双向循环链表里实现这个功能是十分简单的!并且时间复杂度为O(1)效率特别高!

代码:

void popBack(ListNode* head)
{
	assert(head);
	ListNode* del = head->prev;//记录一下要删除的尾节点
	ListNode* tailFront = del->prev;//找到尾结点的前一个节点

	tailFront->next = head;
	head->prev = tailFront;

	free(del);
	del = NULL;
}

第六个功能的实现——头删

头删的操作与尾删差不多,只不过删尾变成了删头。

void popFront(ListNode* head)
{
	assert(head);
	ListNode* del = head->next;
	ListNode* next = del->next;

	head->next = next;
	next->prev = head;

	free(del);
	del = NULL;
}

 第七个功能——中间删除

中间删除是包含头删与尾删的其实,假如你要将头删与尾删的功能用一个函数进行实现的话那就可以实现一个中间删除函数。

void ListErase(ListNode* pos)//中间删除,删除pos后面的节点,但是不能删除哨兵位的头
{
	assert(pos);
	ListNode* del = pos;

	ListNode* Frontnode = del->prev;
	ListNode* next = pos->next;

	next->prev = Frontnode;
	Frontnode->next = next;

	free(del);
	del = NULL;
}
当你要变成一个头删函数时,这个中间删除的函数的参数改变一下就行了。
ListErase(head->next);//头删

ListErase(head->prev);//尾删

 但是当你要删除一个既不是头也不是尾的节点时,你就要再弄一个查找函数。

ListNode* ListFind(ListNode* pHead, LTDataType x)
{
	assert(pHead);
	ListNode* cur = pHead;
	while (cur->next != pHead)
	{
		if (cur->val == x)
		{
			return cur;
		}
		cur = cur->next;
	}
	if (cur->val == x)
	{
		return cur;
	}
	return NULL;
}

用这个函数定位到你要删除的元素,然后再将其删除!但是返回空指针的时候是不能进行删除的!

最后一个功能——销毁链表

因为你的链表的节点是malloc出来的,所以你要在用完以后将其销毁来避免内存泄漏。

void ListDestory(ListNode* head)
{
	ListNode* cur = pHead->next;
	ListNode* next = cur->next;
	while (cur !=pHead) {
		free(cur);
		cur = next;
		next = cur->next;
	}
	printf("销毁成功!\n");
}

 

 

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

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

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

相关文章

  • 数据结构:链表基础OJ练习+带头双向循环链表的实现

    目录 一.leetcode剑指 Offer II 027. 回文链表 1.问题描述 2.问题分析与求解 (1) 快慢指针法定位链表的中间节点 (2) 将链表后半部分进行反转 附:递归法反转链表 (3) 双指针法判断链表是否回文 二.带头双向循环链表的实现 1.头文件 2.节点内存申请接口和链表初始化接口 3.链表的打

    2024年02月02日
    浏览(42)
  • 链表的极致——带头双向循环链表

    ​ 双向带头循环链表是链表结构最复杂,但使用最方便的链表。 [图中phead表示链表的头节点(哨兵); d1,d2等表示链表存储的数据; d1,d2等左侧的方框存储是指向该节点的上一个节点的指针(prev),右侧方框存储指向该节点的下一个的指针(next)] 双向带头循环链表的每

    2024年04月10日
    浏览(69)
  • 来领略一下带头双向循环链表的风采吧

    🍉 博客主页:阿博历练记 📖文章专栏:数据结构与算法 🚍代码仓库:阿博编程日记 🌹欢迎关注:欢迎友友们点赞收藏+关注哦 带头双向循环链表:结构最复杂,一般用在单独存储数据。实际中使用的链表数据结构,都是带头双向循环链表。另外这个结构虽然结构复杂,但

    2024年02月05日
    浏览(69)
  • 【数据结构】链表:带头双向循环链表的增删查改

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

    2024年02月05日
    浏览(83)
  • 【数据结构】带头双向循环链表---C语言版(单链表我们分手吧,不要再找我玩了!!!)

    在前面我们学习实现了单链表(无头单向不循环链表),这里我们引入带头双向循环链表 很明显这两种链表的结构截然不同,但都是作为链表最常使用链表结构 前者因其结构上的缺点而作为面试考题的常驻嘉宾,而且复杂麻烦 后者则是以结构最优著称,实现起来也是非常的

    2024年02月10日
    浏览(34)
  • 实现带头双向循环链表

    描述:一个节点内包含两个指针,一个指向上一个节点,另一个指向下一个节点。哨兵位指向的下一个节点为头节点,哨兵位的上一个指向尾节点。 结构优势:高效率找尾节点;高效率插入与删除;无需判断多种复杂情况,如尾节点、空节点等。 BuyListNode节点创建函数()

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

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

    2024年02月10日
    浏览(40)
  • 带头 + 双向 + 循环链表增删查改实现

    目录 源码: List.c文件: List.h文件: 简单的测试: 很简单,没什么好说的,直接上源码。

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

    目录 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日
    浏览(39)
  • 数据结构: 线性表(带头双向循环链表实现)

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

    2024年02月14日
    浏览(33)

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

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

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

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

二维码1

领取红包

二维码2

领红包