当我与单链表分手后,在酒吧邂逅了双向循环链表.....

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

链表的种类有8种,但我们最常用的为无头单向非循环链表带头双向循环链表

当我与单链表分手后,在酒吧邂逅了双向循环链表.....

带头双向循环链表 

当我与单链表分手后,在酒吧邂逅了双向循环链表.....

当带头双向循环链表只有哨兵位头的时候,双向链表的指向如下图。

当我与单链表分手后,在酒吧邂逅了双向循环链表.....

head->pre和head->next都是指向自己,这个是有巨大优势的,代码实现会很方便。 

1.无头单向非循环链表结构简单,一般不会单独用来存数据。实际中更多是作为其他数据结
构的子结构,如哈希桶、图的邻接表等等。另外这种结构在笔试面试中出现很多。
2. 带头双向循环链表结构最复杂,一般用在单独存储数据。实际中使用的链表数据结构,都
是带头双向循环链表。另外这个结构虽然结构复杂,但是使用代码实现以后会发现结构会带
来很多优势,实现反而简单了,后面我们代码实现了就知道了。

在上个博客实现顺序表和单链表的时候我提过这两点,让我们来进入双向循环链表的实现吧!

带头双向循环链表的结点开辟

LTNode* buynode(SedListtype x)
{
	LTNode* newnode = (LTNode*)malloc(sizeof(LTNode));
	if (newnode == NULL)
	{
		perror("malloc fail");
		return NULL;
	}
	newnode->data = x;
	newnode->pre = NULL;
	newnode->next = NULL;

	return newnode;


}

这个开辟结点应该没有什么好说的,带头双向循环链表多了个指针pre。 

带头双向循环链表的初始化

LTNode* LTInit()
{
	LTNode* phead= buynode(-1);
	phead->pre = phead;
	phead->next = phead;
	return phead;

}

哨兵位的结点值是可以随便给的,毕竟只负责站岗,当双向链表为空,phead->pre和phead->next都指向自己,这才是双向循环链表的核心!这里为什么要返回头指针,因为为了与其他增删查改方法实现统一使用一级指针要不然初始化双向链表需要使用二级指针(因为结构体指针是一级指针,为了改变结构体的内容就要传值并用二级指针接受一级指针(结构体指针)才能改变结构体内容),但为了避免问题复杂化,二级指针变为一级指针最简单方法就是自己返回值。

带头双向循环链表的尾插

void LTPushBack(LTNode* phead, SedListtype x)
{
	assert(phead);
	
	LTNode* newnode = buynode(x);
	LTNode* tail = phead->pre;
	tail->next = newnode;
	newnode->pre = tail;
	newnode->next = phead;
	phead->pre = newnode;
}

不用像单链表一样找尾tail,phead->pre就是tail,而且单链表需要判断链表为空不为空的两种情况且需要2级指针,不像带头双向循环链表带有哨兵位的头指针使用一级指针就可以,如果链表为空,这段代码也同样适用,即是头也是尾,双向链表改变的是结构体,用结构体指针就够了,不需要二级指针。

带头双向循环链表为空时

当我与单链表分手后,在酒吧邂逅了双向循环链表.....

带头双向循环链表不为空时 

当我与单链表分手后,在酒吧邂逅了双向循环链表.....

带头双向循环链表的头插

void LTPushFront(LTNode* phead, SedListtype x)
{
	assert(phead);
	LTNode* newnode = buynode(x);
	LTNode* frist = phead->next;
	phead->next = newnode;
	newnode->pre = phead;
	newnode->next = frist;
	frist->pre = newnode;


}

错误写法
Void LTPushFront(LTNode* phead, LTDataType x)
assert(phead);
LTNode* newnode = BuyLTNode(x);
phead->next = newnode;
newnode->prev = phead;
newnode->next = phead->next;
phead->next->prev = newnode;
最常见错误之一,会找不到phead->next的原来的地址值

调整一下顺序即可
void LTPushFront(LTNode* phead, LTDataType x)
assert(phead);
LTNode* newnode = BuyLTNode(x);
newnode->next = phead->next;
phead->next->prev = newnode;
phead->next = newnode;
newnode->prev = phead;

先保存phead->next头结点的值,避免找不到或者改变phead的值,然后再按照常规连接即可。

当链表为空,这段代码也一样适用,因为双向链表的核心就是头指针的pre和next都是指向自己,这是带头双向循环链表的优势。

当我与单链表分手后,在酒吧邂逅了双向循环链表..... 

布尔值判断

bool LTEmpty(LTNode* phead)
{
	assert(phead);

	return phead->next == phead;
}

这是为了判断在进行头删和尾删的时候,不能把哨兵位(头指针)给删除掉 ;布尔值就是布尔值是“真” True 或“假” False 中的一个,如果删除到哨兵位(头指针)就返回头指针本身。

带头双向循环链表的尾删

void LTPopBack(LTNode* phead)
{
	assert(phead);
    assert(!LTEmpty(phead));
	LTNode* tail = phead->pre;
	LTNode* pre = tail->pre;
	free(tail);
	pre->next = phead;
	phead->pre = pre;
}

assert(!LTEmpty(phead));是为了防止删除哨兵位(头结点)。

先找尾tail,然后tail->pre就是上一个结点pre的值,再释放掉尾tail,最后常规连接即可。 

当我与单链表分手后,在酒吧邂逅了双向循环链表.....

带头双向循环链表的头删

void LTPopFront(LTNode* phead)
{
	assert(phead);
    assert(!LTEmpty(phead));
	LTNode* frist = phead->next;
	LTNode* second = frist->next;

	phead->next = second;
	second->pre = phead;
	free(frist);

}

assert(!LTEmpty(phead));是为了防止删除哨兵位(头结点)。 

头删的代码肯定不止一种写法,但我们这种写法是最通俗易懂且简便的,可以让别人一眼就能看出我们的意图所在,先用frist存phead->next的值,再用second存frist->next的值,然后常规连接释放即可。

当我与单链表分手后,在酒吧邂逅了双向循环链表.....

带头双向循环链表的查找

LTNode* Find(LTNode* phead, SedListtype x)
{
	assert(phead);
	LTNode* cur = phead->next;
	while (cur != phead)
	{
		if (cur->data == x)
		{
			return cur;
		}
		cur = cur->next;
	}
	return NULL;
}

当cur不等于phead(哨兵位)时就遍历完了链表,这个应该没啥好说的,各位兄弟姐妹也会自己写。但这个查找遍历与顺序表和单链表都一样,查找链表内对应值的内容是可以修改的。 

 带头双向循环链表的插入(pos之前插入)

void LTInsert(LTNode* pos, SedListtype x)
{
	assert(pos);
	LTNode* newnode = buynode(x);
	LTNode* prepos = pos->pre;
	prepos->next = newnode;
	newnode->pre = prepos;
	newnode->next = pos;
	pos->pre = newnode;


}

先用pos->pre找到pos上一个结点的值,再用一个临时变量存储,再常规连接即可。 

当我与单链表分手后,在酒吧邂逅了双向循环链表.....

  带头双向循环链表的删除(pos位置删除)

void LTErase(LTNode* pos)
{
	assert(pos);
	LTNode* pospre = pos->pre;
	LTNode* posnext = pos->next;
	pospre->next = posnext;
	posnext->pre = pospre;
	free(pos);

}

 利用pos找出pos前面与后面结点的值,用两个临时变量存储,常规连接两个变量即可。

当我与单链表分手后,在酒吧邂逅了双向循环链表.....

 带头双向循环链表的打印

void LTprint(LTNode* phead)
{
	assert(phead);
	LTNode* cur = phead->next;
	printf("guard<==>");哨兵位
	while (cur != phead)
	{
		printf("%d<==>", cur->data);
		cur = cur->next;
	}
	printf("\n");


}

  带头双向循环链表的销毁

void LTDestory(LTNode* phead)
{
	LTNode* cur = phead->next;
	while (cur != phead)
	{
		LTNode* next = cur->next;
		free(cur);
		cur = next;
	}
	free(phead);
}

用next存储结点值,再一个个释放即可。 

使用完动态开辟的结点值后要销毁,要不然会内存泄漏,phead可以在test.c文件中再置空,因为释放掉以后,不会再有人使用,但为了安全最好在test.c置空一下即可。

带头双向循环链表的完整代码以及运行结果

SedList.h
#include<stdio.h>
#include<assert.h>
#include<stdlib.h>
#include<stdbool.h>
typedef int SedListtype;
typedef struct SedList
{
	struct SedList* pre;
	struct SedList* next;
	SedListtype data;

}LTNode;

bool LTEmpty(LTNode* phead);//布尔值防止删除哨兵位
LTNode* LTInit();//初始化;
void LTprint(LTNode* phead);//打印
void LTDestory(LTNode* phead);//销毁

void LTPushBack(LTNode* phead, SedListtype x);//尾插
void LTPushFront(LTNode* phead, SedListtype x);//头插

void LTPopBack(LTNode* phead);//尾删
void LTPopFront(LTNode* phead);//头删

LTNode* Find(LTNode* phead,SedListtype x);//查找

void LTInsert(LTNode* pos, SedListtype x);//pos之前插入
void LTErase(LTNode* pos);//pos位置删除
SedList.c
#include"sedlist.h"
LTNode* buynode(SedListtype x)
{
	LTNode* newnode = (LTNode*)malloc(sizeof(LTNode));
	if (newnode == NULL)
	{
		perror("malloc fail");
		return NULL;
	}
	newnode->data = x;
	newnode->pre = NULL;
	newnode->next = NULL;

	return newnode;


}
bool LTEmpty(LTNode* phead)
{
	assert(phead);

	return phead->next == phead;
}

LTNode* LTInit()
{
	LTNode* phead= buynode(-1);
	phead->pre = phead;
	phead->next = phead;
	return phead;

}
void LTprint(LTNode* phead)
{
	assert(phead);
	LTNode* cur = phead->next;
	printf("guard<==>");//哨兵位
	while (cur != phead)
	{
		printf("%d<==>", cur->data);
		cur = cur->next;
	}
	printf("\n");


}
void LTDestory(LTNode* phead)
{
	LTNode* cur = phead->next;
	while (cur != phead)
	{
		LTNode* next = cur->next;
		free(cur);
		cur = next;
	}
	free(phead);
}

void LTPushBack(LTNode* phead, SedListtype x)
{
	assert(phead);
	
	LTNode* newnode = buynode(x);
	LTNode* tail = phead->pre;
	tail->next = newnode;
	newnode->pre = tail;
	newnode->next = phead;
	phead->pre = newnode;

}

void LTPushFront(LTNode* phead, SedListtype x)
{
	assert(phead);
	LTNode* newnode = buynode(x);
	LTNode* frist = phead->next;
	phead->next = newnode;
	newnode->pre = phead;
	newnode->next = frist;
	frist->pre = newnode;


}

void LTPopBack(LTNode* phead)
{
	assert(phead);
	LTNode* tail = phead->pre;
	LTNode* pre = tail->pre;
	free(tail);
	pre->next = phead;
	phead->pre = pre;
}
void LTPopFront(LTNode* phead)
{
	assert(phead);
	LTNode* frist = phead->next;
	LTNode* second = frist->next;

	phead->next = second;
	second->pre = phead;
	free(frist);



}
LTNode* Find(LTNode* phead, SedListtype x)
{
	assert(phead);
	LTNode* cur = phead->next;
	while (cur != phead)
	{
		if (cur->data == x)
		{
			return cur;
		}
		cur = cur->next;
	}
	return NULL;
}

void LTInsert(LTNode* pos, SedListtype x)
{
	assert(pos);
	LTNode* newnode = buynode(x);
	LTNode* prepos = pos->pre;
	prepos->next = newnode;
	newnode->pre = prepos;
	newnode->next = pos;
	pos->pre = newnode;


}
void LTErase(LTNode* pos)
{
	assert(pos);
	LTNode* pospre = pos->pre;
	LTNode* posnext = pos->next;
	pospre->next = posnext;
	posnext->pre = pospre;
	free(pos);

}
test.c
#include"sedlist.h"
void  test1()
{
	LTNode* plist = LTInit();
	LTPushBack(plist, 1);
	LTPushBack(plist, 2);
	LTPushBack(plist, 3);

	LTprint(plist);
	LTDestory(plist);
	plist = NULL;

}
void  test2()
{
	LTNode* plist = LTInit();
	LTPushFront(plist, 1);
	LTPushFront(plist, 2);
	LTPushFront(plist, 3);

	LTprint(plist);
	LTDestory(plist);
	plist = NULL;

}
void  test3()
{
	LTNode* plist = LTInit();
	LTPushBack(plist, 1);
	LTPushBack(plist, 2);
	LTPushBack(plist, 3);
	LTPopBack(plist);
	LTPopBack(plist);
	LTPushBack(plist, 4);
	LTprint(plist);
	LTDestory(plist);
	plist = NULL;

}
void test4()
{
	LTNode* plist = LTInit();
	LTPushFront(plist, 1);
	LTPushFront(plist, 2);
	LTPushFront(plist, 3);
	LTPopFront(plist);
	LTPopFront(plist);
	LTPushFront(plist, 5);
	LTprint(plist);
	LTDestory(plist);
	plist = NULL;

	


}
void test5()
{
	LTNode* plist = LTInit();
	LTPushFront(plist, 1);
	LTPushFront(plist, 2);
	LTPushFront(plist, 3);
	LTPushFront(plist, 5);
	LTNode* pos = Find(plist, 2);
	pos->data = 12;
	LTprint(plist);
	LTDestory(plist);
	plist = NULL;




}
void test6()
{
	LTNode* plist = LTInit();
	LTPushFront(plist, 1);
	LTPushFront(plist, 2);
	LTPushFront(plist, 3);
	LTPushFront(plist, 5);
	LTNode* pos = Find(plist, 2);
	if (pos)
	{
		LTInsert(pos, 24);
	}
	LTprint(plist);
	LTDestory(plist);
	plist = NULL;




}
void test7()
{
	LTNode* plist = LTInit();
	LTPushFront(plist, 1);
	LTPushFront(plist, 2);
	LTPushFront(plist, 3);
	LTPushFront(plist, 5);
	LTNode* pos = Find(plist, 3);
	if (pos)
	{
		LTErase(pos);
	}
	LTprint(plist);
	LTDestory(plist);
	plist = NULL;

}
int main()
{
	test1();
	test2();
	test3();
	test4();
	test5();
	test6();
	test7();

	return 0;
}

当我与单链表分手后,在酒吧邂逅了双向循环链表.....文章来源地址https://www.toymoban.com/news/detail-446879.html

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

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

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

相关文章

  • 【网络奇幻之旅】那年我与互联网的邂逅

    🌺 个人主页: Dawn黎明开始 🎀 系列专栏: 网络奇幻之旅 ⭐ 每日一句:不想留在过去,就要变得更好 📢 欢迎大家:关注 🔍+ 点赞 👍+评论 📝+ 收藏⭐️ 文章目录 📋前言 一、互联网的定义和分类 二、互联网的特点 三、互联网的应用 四、互联网的负面影响及防护措施

    2024年02月04日
    浏览(41)
  • 单链表与循环链表创建

    单链表 实现 错例 在使用malloc函数开辟的空间中,不要进行指针的移动, 因为一旦移动之后可能出现申请的空间和释放空间大小的不匹配 循环链表 单独创建 实现方式一: 实现方式二:

    2024年01月18日
    浏览(22)
  • 循环链表详解(循环单链表/循环双链表)

    目录 一、循环单链表 二、循环双链表 循环单链表的表尾结点的next指针总是指向头结点。  所以在初始化循环单链表的时候,需要记得将头结点的next指针指向头结点自己:   判断循环单链表是否为空 ,只要判断头结点的next指针是否指向自己就可以了,因为循环链表的最后

    2024年02月09日
    浏览(43)
  • 单向-->不带头-->非循环链表(简称:单链表)

    目录 一、链表的介绍 1.链表的概念 2.单链表的节点类型 3.单链表简图 二、单链表的增删查改 1.单链表的头插 2.单链表的尾插 3.单链表的头删 4.单链表的尾删 5.单链表pos位置之后插入一个节点 6.单链表删除pos位置后的一个节点         链表是一种物理存储结构上非连续、非顺

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

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

    2024年02月10日
    浏览(44)
  • 双向-->带头-->循环链表

    目录 一、双向带头循环链表概述 1.什么是双向带头循环链表 2.双向带头循环链表的优势 3.双向带头循环链表简图 二、双向带头循环链表的增删查改图解及代码实现 1.双向带头循环链表的头插 2.双向带头循环链表的尾插 3.双向带头循环链表的头删 4.双向带头循环链表的尾删

    2024年02月12日
    浏览(43)
  • 带头双向循环链表

    前言:当我们想要挪动大量数据的时候,单链表的效率就很低了,比如尾插,要找到尾,通过遍历的时间复杂度最坏情况是O(N),所以我们引进结构复杂但效率高的双向带头循环链表 带头双向循环链表指得是什么呢?就是既存储了上一个节点的地址,又存储了下一个节点的地址

    2024年04月23日
    浏览(19)
  • 双向带头循环链表

    所属专栏:初始数据结构 博主首页:初阳785 代码托管:chuyang785 感谢大家的支持,您的点赞和关注是对我最大的支持!!! 博主也会更加的努力,创作出更优质的博文!! 关注我,关注我,关注我,重要的事情说三遍!!!!!!!! 我们之前也已经学完了单链表,并且也

    2024年02月05日
    浏览(28)
  • 链表(2)——带头双向循环链表

    目录 🍁一、链表的分类 🌕1.单向或者双向 🌕2.带头或者不带头(有无哨兵) 🌕3.循环或者不循环 🌕4.无头单向非循环链表(常用) 🌕5.带头双向循环链表(常用) 🌕注意: 🍁二、双向链表的定义: 🍁三、带头双向循环链表的定义 🍁四、带头双向循环链表操作实现(

    2024年02月08日
    浏览(23)
  • 双向循环链表的接口

    在链表的头部插入 在链表的尾部插入 在链表指定数据节点后插入 删除链表头部的节点 删除链表尾部的节点 删除链表指定数据节点后的节点

    2024年04月25日
    浏览(20)

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

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

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

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

二维码1

领取红包

二维码2

领红包