带头结点的双向循环链表

目录

带头结点的双向循环链表

 1.存储定义

2.结点的创建

3.结点的初始化

4.尾插结点

5.尾删结点

6.头插结点

7.头删结点 

8.查找并返回结点

9.在pos结点前插入结点

10.删除pos结点

11.打印链表

 12.销毁链表

13.头插结点2.0版

 14.尾插结点2.0版


前言:

当我们使用单链表时,想要去找到前面的前驱结点的时候,我们发现了受到限制,因为当时的结点只能去找后面的结点。

于是,就到了这里的带头结点的双向循环链表

如图:

首先给出  双向链表的定义:是在单链表的每个结点中,再设置一个指向其前驱结点的指针域。

那双向循环链表就是最后一个结点的next指向头结点,头结点的前驱指向尾结点。

带头结点的双向循环链表

 1.存储定义

typedef int CLDataType;

typedef  struct ListNode
{
	CLDataType val;
	struct ListNode* next; //存放后面结点的指针
	struct ListNode* pre;  //存放前面结点的指针
}ListNode;

2.结点的创建

结点的创建,刚开始创建头结点我们一般会采用二级指针的方式,这里我们采用使用函数返回值的形式来避免野指针问题。

//创建链表
ListNode* CreateNode(CLDataType x) 
{
	//这里采用返回值的形式进行创建结点
	ListNode* newnode = (ListNode*)malloc(sizeof(ListNode));
	if (newnode == NULL) 
	{
		perror("malloc");
		exit(-1);
	}
	newnode->val = x;
	newnode->pre = NULL;
	newnode->next = NULL;
	return newnode;
}

 如果是刚开始创建的头节点的话:

3.结点的初始化

这个时候就能体现带头结点双向循环链表的好处了

ListNode* InitNode() 
{
	ListNode* phead = CreateNode(-1);
	//让头节点的前驱和后继结点都指向自己
	phead->next = phead;	
	phead->pre = phead;
	return phead;
}

 因为修改了头结点,所以接着使用函数返回值的形式。

注意:初始化时,头结点的前驱和后继指针都指向自己。如图:

4.尾插结点

之前的单链表尾插需要遍历整个链表去找尾结点,但是,这里是带头结点的双向循环链表,只需去找头结点的前驱结点就找到了尾结点。

//尾插结点
void ListPushBack(ListNode* phead, CLDataType x) 
{
	assert(phead);
	ListNode* newnode = CreateNode(x);
	ListNode* tail = phead->pre;
	
	tail->next = newnode; //之前的尾结点的后继指针指向新结点
	newnode->pre = tail;  //新结点的前驱指向之前的尾结点
	newnode->next = phead; //新结点的后继指针指向头结点
	phead->pre = newnode;  //头结点的前驱指向

 当链表中只有一个头结点的时候,tail指向自己。

 再继续尾插时,仍是这样。

5.尾删结点

 一般都是先找到尾节结点,然后再去找到尾结点的前一个结点Pre。Pre指向头结点,头结点的前驱指向Pre。之后再删去尾节结。

//尾删结点
void ListPopBack(ListNode* phead)
{
	assert(phead);
	assert(phead->next != phead); //避免只有一个头结点
	ListNode* tail = phead->pre;
	ListNode* Pre = tail->pre; //用来记录尾结点的前一个结点
	Pre->next = phead;
	phead->pre = Pre;
	free(tail);
	tail = NULL;
}

注意:当只有一个头结点的情况时,说明已经没有链表结点。所以不能进行删除。进而这里采用,assert( phead->next != phead) 来避免此情况。

6.头插结点

和单链表的头插相似,用next指针记录头结点的下一个结点。先让新结点与next指针指向的结点建立连接,再与头结点建立连接。

//头插结点
void ListPushFront(ListNode* phead,CLDataType x) 
{
	assert(phead);
	ListNode* newnode = CreateNode(x);
	ListNode* next = phead->next; //记录头结点后的第一个结点

	newnode->next = next;
	next->pre = newnode;

	phead->next = newnode;
	newnode->pre = phead;
}

7.头删结点 

这里需要注意的是避免只有一个头结点的情况下,删除结点,这样使用同尾删结点一样的方法,通过断言 assert(phead->next != phead);

//头删结点
void ListPopFront(ListNode* phead) 
{
	assert(phead);
	assert(phead->next != phead);
	phead->next = phead->next->next;
	phead->next->next->pre = phead;
}

8.查找并返回结点

这里采用cur指针去遍历链表,找到就返回结点,没有找到就返回空。

//查找并返回结点
ListNode* ListFind(ListNode* phead, CLDataType x) 
{
	assert(phead);
	ListNode* cur = phead->next;
	while (cur != phead) 
	{
		if (cur->val == x)
		{
			return cur;
		}
		cur = cur->next;
	}
	return NULL;
}

9.在pos结点前插入结点

因为是带头结点的双向循环链表,这样就可以直接进行插入。

//在pos前面插入结点
void ListInsert(ListNode* pos, CLDataType x) 
{
	assert(pos);
	ListNode* newnode = CreateNode(x);
    //注意顺序
    //先让后驱建立连接
	newnode->next = pos;
	pos->pre->next = newnode;
    //再前驱建立连接
	newnode->pre = pos->pre;
	pos->pre = newnode;
}

10.删除pos结点

 修改pos的前驱和后继

//删除pos位置的结点
void ListErase(ListNode* pos) 
{
	assert(pos);
	pos->pre->next = pos->next;
	pos->next->pre = pos->pre;
}

11.打印链表

//打印链表
void ListPrint(ListNode* phead) 
{
	assert(phead);
	ListNode* cur = phead->next;
	printf("phead<==>");
	while (cur != phead) 
	{
		printf("%d<==>", cur->val);
		cur = cur->next;
	}
	printf("\n");
}

 12.销毁链表

//销毁链表
void ListDestory(ListNode* phead) 
{
	assert(phead);
	ListNode* cur = phead->next;
	while (cur != phead)
	{
		ListNode* next = cur->next;
		free(cur);
		cur = next;
	}
	free(phead);
	phead = NULL;
}

13.头插结点2.0版

这里我们借助 在pos结点前插入结点 的接口。

头插结点,那就是把pos结点变为 phead->next 的结点。这样结点的插入就模拟了头插结点,从而减少了一写代码的工作量。

//头插结点2.0
void ListushFront(ListNode* phead, CLDataType x)
{
	assert(phead);
	ListNode* newnode = CreateNode(x);
	ListNode* next = phead->next; //记录头结点后的第一个结点
	ListInsert(next,x); //这里的next相当于pos结点
}

例如  :在 1结点 前插入 结点25

 

 14.尾插结点2.0版

根据带头结点的双向循环链表的特性,再借助前面的 在pos结点前插入结点(ListInsert) 的接口

这时我们会想到的就是 尾插结点不是在尾结点的后面进行插入的吗,而ListInsert是在pos前面进行结点的插入。 这时就采用  在头结点的前进行插入结点,从而达到尾插结点的特性。

//尾插结点
void ListPushBack(ListNode* phead, CLDataType x)
{
	assert(phead);
	ListNode* newnode = CreateNode(x);
	ListInsert(phead,x);
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/207635.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

go开发之个人微信号机器人开发

简要描述&#xff1a; 下载消息中的文件 请求URL&#xff1a; http://域名地址/getMsgFile 请求方式&#xff1a; POST 请求头Headers&#xff1a; Content-Type&#xff1a;application/jsonAuthorization&#xff1a;login接口返回 参数&#xff1a; 参数名必选类型…

IO / day01 作业。

1.使用fgets统计一个文件的行号 //使用fgets统计一个文件的行号#include <string.h> #include <stdlib.h> #include <stdio.h>int main(int argc, const char *argv[]) {if(argc<2) //获取文件名{printf("input error\n!");printf("usage…

Linux系统的常见命令十一,文本编辑器(vi和vim)

目录 vi命令vim命令vi命令与vim命令的区别 本文主要介绍Linux系统的文本编辑器命令vi和vim&#xff0c;还有它们之间的区别。 vi命令 vi是Linux和其他类Unix操作系统中最常用的文本编辑器之一&#xff0c;它的功能强大且灵活&#xff0c;可以通过键盘快捷键来完成大量的编辑操…

【数据结构】线段树

目录 1.概述2.代码实现2.1.聚合操作——求和2.2.聚合操作——求和、求最小值、求最大值 3.应用4.与前缀和之间的区别 更多数据结构与算法的相关知识可以查看数据结构与算法这一专栏。 1.概述 &#xff08;1&#xff09;线段树 (Segment Tree) 是一种二叉树形数据结构&#xff…

算法通关村第一关—白银挑战—链表高频面试算法题—查找两个链表的第一个公共子节点

文章目录 查找两个链表的第一个公共子节点&#xff08;1&#xff09;暴力求解法&#xff08;2&#xff09;使用哈希Hash⭐&#xff08;3&#xff09;使用集合⭐ - 与Hash类似&#xff08;4&#xff09;使用栈⭐&#xff08;5&#xff09;仍有更多方法&#xff0c;作者尚未理解&…

安卓小程序与编译抓包

APK小程序渗透测试 查找bp的证书 在浏览器中打开bp代理&#xff0c;然后在网页中搜索hppps://burp 点击高级——接受风险并继续 拿到证书 将浏览器信任证书 打开设置 搜索证书——查看证书 点击导入——导入证书 证书验证成功后&#xff0c;访问网页&#xff08;吾爱破解&a…

模型层——单表操作

单表操作 一 ORM简介 查询数据层次图解&#xff1a;如果操作mysql&#xff0c;ORM是在pymysq之上又进行了一层封装 MVC或者MTV框架中包括一个重要的部分&#xff0c;就是ORM&#xff0c;它实现了数据模型与数据库的解耦&#xff0c;即数据模型的设计不需要依赖于特定的数据库…

河南省第一届职业技能大赛网络安全项目试题

河南省第一届职业技能大赛 网络安全项目试题 一、竞赛时间 总计&#xff1a;420分钟 竞赛阶段 竞赛阶段 任务阶段 竞赛任务 竞赛时间 分值 A模块 A-1 登录安全加固 240分钟 200分 A-2 Web安全加固&#xff08;Web&#xff09; A-3 流量完整性保护与事件监控&am…

韩语语法中에和로/으로区别,柯桥发音入门韩语培训学校

에和로/으로在行动的去向与到达或涉及的地点一致时&#xff0c;二者可以互换。 但是에表示到达或涉及的具体地点&#xff0c;而로/으로表示的时动作指向的方向或经过的地点。 在只表示去向而不表示具体地点时&#xff0c;只能用로/으로&#xff0c;而在只表示具体地点而不表示方…

Nginx漏洞修复

1、漏洞 去掉在请求响应头中存在的信息 Server: nginx X-Content-Type-Options: nosniff X-Frame-Options: SAMEORIGIN X-XSS-Protection: 1;modeblock 修复方法 在Nginx的配置文件中的 server 标签内增加一下配置 server_tokens off; add_header X-Frame-Options SAMEORIGIN; …

情绪咖啡亭完美收官!来最美环湖路喝一杯“治愈”咖啡

“芭比粉”的主题墙&#xff0c;橙蓝撞色的情绪日历、当下最流行的克莱因蓝咖啡亭......颜色鲜艳&#xff0c;造型吸睛的“情绪咖啡亭”互动艺术装置区与碧树蓝天、海鸥白云相呼应。春城晚报&#xff08;开屏新闻&#xff09;生活节“送服务”系列之一的“情绪咖啡亭”活动将在…

jetson nano SSH远程连接(使用MobaXterm)

文章目录 SSH远程连接1.SSH介绍2.准备工作3.连接步骤3.1 IP查询3.2 新建会话和连接 SSH远程连接 本节课的实现&#xff0c;需要将Jetson Nano和电脑保持在同一个局域网内&#xff0c;也就是连接同一个路 由器&#xff0c;通过SSH的方式来实现远程登陆。 1.SSH介绍 SSH是一种网…

字符集与编码规则

字符集 强调&#xff1a;UTF-8是编码规则&#xff0c;不是字符集 过程&#xff1a; 字符 --查表获得对应数字&#xff0c;--编码 解码---查表----获取字符 ASCII码 &#xff1a;一个字节 8bit GBK字符集&#xff08;windows系统默认使用的GBK,系统显示ANSI&#xff09; 存…

segment-anything安装教程

文章目录 一. segment-anything安装教程 一. segment-anything安装教程 官网安装说明:https://github.com/facebookresearch/segment-anything anaconda下新建一个环境 conda create -n sam python3.8激活新建的环境 conda activate sam更换conda镜像源 conda config --add ch…

如何靠掌握自己的大数据打破信息流的壁垒?

在当今数字化时代&#xff0c;打造自己的私域流量已经成为商家乃至获取竞争优势的关键手段之一。通过掌握自己的大数据&#xff0c;可以更好地了解用户需求和市场趋势&#xff0c;优化产品和服务&#xff0c;从而打破信息流的壁垒。本文将就如何通过打造自己的私域流量并掌握大…

ROS URDF集成Rviz流程

实现流程&#xff1a; 一、新建功能包&#xff0c;导入依赖 二、编写 urdf 文件 三、在 launch 文件集成 URDF 与 Rviz 四、在 Rviz 中显示机器人模型 需求&#xff1a;在 Rviz 中显示一个盒状机器人 1、创建功能包&#xff0c;导入依赖 创建一个新的功能包&#xff0c;名…

VMWare17配置自动启动虚拟机提示:无法更新“自动启动配置”,请确保存在vmAutoStart.xml文件,并且您有权写入此文件。

文章目录 配置的时候提示&#xff1a;无法更新“自动启动配置”&#xff0c;请确保存在vmAutoStart.xml文件&#xff0c;并且您有权写入此文件。需要修改vmAutoStart.xml这个文件权限对vmautostart.xml文件右键-->属性&#xff0c;选择编辑直接将完全控制的允许勾上&#xf…

工程师每日刷题 -4

文章目录 1、深度学习2、算法与数据结构2.1、暴力解法2.2、滑动窗口法 3、编程基础 1、深度学习 问题&#xff1a;CNN的本质和优势&#xff1f; CNN 本质上是一个多层感知机 (MLP)&#xff0c;其成功的原因关键在于它所采用的【稀疏连接】&#xff08;局部感受&#xff09;和…

Android Studio Giraffe | 2022.3.1

Android Gradle 插件和 Android Studio 兼容性 Android Studio 构建系统以 Gradle 为基础&#xff0c;并且 Android Gradle 插件 (AGP) 添加了几项专用于构建 Android 应用的功能。下表列出了各个 Android Studio 版本所需的 AGP 版本。 Android Studio 版本所需的 AGP 版本I…

Python+Requests模块session处理和SSL证书处理关闭警告

session处理 部分接口需要先登录网址&#xff0c;才能有权限进行调用&#xff0c;这时可以使用到session&#xff0c;具体操作是&#xff1a;先使用网站 的登录api进行登录&#xff0c;得到session后&#xff0c;然后用该session来请求其它的接口。 示例代码&#xff1a; se…