带头双向循环链表的实现

带头双向循环链表的实现

在这里插入图片描述

文章目录

  • 带头双向循环链表的实现
    • 一、模型构建
    • 二、代码实现(接口函数以及测试用例)
      • ①初始化 + Create函数
      • ②尾插
      • ③尾删
      • ④头插
      • ⑤头删
      • ⑥查找
      • ⑦在pos位置前插入
        • 新尾插
        • 新头插
      • ⑧删除pos位
        • 新尾删
        • 新头删
      • ⑨销毁链表
      • ⑩打印链表
      • ⑪测试用例
    • 三、链表和顺序表的对比

一、模型构建

在这里插入图片描述
每一个结点都有一个前置指针,前置指针存上一个结点的地址,一个后置指针存下一个结点的地址,还有一个存储数据的变量。尾的下一个就是头,头的上一个就是尾。
并且首尾相连还带有哨兵位头结点。
充分体现出了双向带头循环。
如果要是链表为空,那应该是什么样子的呢?
在这里插入图片描述
phead->next = phead, phead->prev = phead.

二、代码实现(接口函数以及测试用例)

①初始化 + Create函数

因为双向带头循环链表无论如何都是有一个哨兵位phead的结点,所以我们初始化的时候不能像创建顺序表和链表一样把指针设置为NULL之后就OK啦,需要用malloc函数去开辟一块空间。

LT* CreateNode(LTDataType x)
{
	LT* newnode = (LT*)malloc(sizeof(LT));
	if (newnode == NULL)
	{
		printf("malloc");
		exit(-1);
	}
	newnode->x = x;
	newnode->next = NULL;
	newnode->prev = NULL;
	return newnode;
}
LT* LTIni()
{
	LT* newnode = CreateNode(-1);
	LT* phead = newnode;
	phead->next = phead;
	phead->prev = phead;
	return phead;
}

②尾插

void LTPushBack(LT* phead, LTDataType x)
{
	assert(phead);
	LT* tail = phead->prev;
	LT* newnode = CreateNode(x);
	tail->next = newnode;
	newnode->prev = tail;
	newnode->next = phead;
	phead->prev = newnode;
}

找尾很方便,直接就是phead->prev

③尾删

void LTPopBack(LT* phead)
{
	assert(phead);
	assert(phead->next != phead);
	LT* tail = phead->prev;
	LT* newtail = tail->prev;
	newtail->next = phead;
	phead->prev = newtail;
	free(tail);
	tail = NULL;
}

④头插

void LTPushFront(LT* phead, LTDataType x)
{
	assert(phead);
	LT* newnode = CreateNode(x);
	LT* tmp = phead->next;
	phead->next = newnode;
	newnode->prev = phead;
	newnode->next = tmp;
	tmp->prev = newnode;
}

⑤头删

void LTPopFront(LT* phead)
{
	assert(phead);
	assert(phead->next != phead);
	LT* node = phead->next;
	phead->next = node->next;
	node->next->prev = phead;
	free(node);
	node = NULL;
}

⑥查找

LT* LTFind(LT* phead, LTDataType x)
{
	assert(phead);
	LT* cur = phead->next;
	while (cur != phead)
	{
		if (cur->x == x)
		{
			return cur;
		}
		cur = cur->next;
	}
	return NULL;
}

⑦在pos位置前插入

void LTInsert(LTDataType x, LT* pos)//在pos位置前插入一个结点
{
	assert(pos);
	LT* tmp = pos->prev;
	LT* newnode = CreateNode(x);
	tmp->next = newnode;
	newnode->prev = tmp;
	newnode->next = pos;
	pos->prev = newnode;
}

大家仔细观察一下如果pos = phead是一个什么情况呢?
我画图为大家解释一下:
在这里插入图片描述

由此可见,如果要是pos = phead,那插入pos位之前就相当于是尾插,
而如果想实现头插,我们也只需把pos位设置成phead->next就可以啦。

新尾插
void LTPushBack(LT* phead, LTDataType x)
{
	assert(phead);
	LTInsert(x, phead);
}
新头插
void LTPopBack(LT* phead LTDataType x)
{
	assert(phead);
	LTInsert(x, phead->next);	
}

⑧删除pos位

void LTErase1(LT* pos)//删除pos位置前的结点
{
	assert(pos);
	LT* tmp = pos->prev;
	tmp->prev->next = pos;
	pos->prev = tmp->prev;
	free(tmp);
	tmp = NULL;
}

void LTErase2(LT* pos)
{
	assert(pos);
	LT* tmp1 = pos->next;
	LT* tmp2 = pos->prev;
	tmp2->next = tmp1;
	tmp1->prev = tmp2;
	free(pos);
	//pos = NULL;
}

LTErase1函数其实并不是很重要,让我们一起来看LTErase2函数,最后这步pos是否设置NULL,其实影响都不是很大,因为形参的改变是不会影响实参的,所以这个时候其实实际的pos已经成为了一个野指针了,我们就需要在工程里使用他的地方后面自己设置成NULL。
例如:
在这里插入图片描述
但是在此处个人认为在这个函数里是不是加上这句断言会更好一些呀

assert(pos != phead)
新尾删
void LTPopBack(LT* phead)
{
	assert(phead);
	assert(phead->next != phead);
	LTErase2(phead->prev);
}
新头删
void PopFront(LT* phead)
{
	assert(phead);
	assert(phead->next);
	LTErase2(phead->next);
}

⑨销毁链表

void LTDestroy(LT* phead)
{
	assert(phead);
	LT* cur = phead->next;
	while (cur != phead)
	{
		LT* next = cur->next;
		free(cur);
		cur = next;
	}
	free(phead);
	//phead = NULL;//外面自己置空,形参的改变不影响实参。
}

销毁和删除一样,动态开辟的内存的指针都需要在外面重新置空

⑩打印链表

void LTPrint(LT* phead)
{
	assert(phead);
	LT* cur = phead->next;
	printf("<=>哨兵位<=>");
	while (cur != phead)
	{
		printf("%d<=>", cur->x);
		cur = cur->next;
	}
	printf("\n");
}

⑪测试用例

#define _CRT_SECURE_NO_WARNINGS
#include "List.h"

void test1()
{
	LT* listnode = LTIni();
	LTPushBack(listnode, 1);
	LTPushBack(listnode, 2);
	LTPushBack(listnode, 3);
	LTPushBack(listnode, 4);
	LTPushBack(listnode, 5);
	LTPrint(listnode);
	LTPopBack(listnode);
	LTPopBack(listnode);
	LTPopBack(listnode);
	LTPopBack(listnode);
	LTPopBack(listnode);
	LTPrint(listnode);
	LTPushFront(listnode, 1);
	LTPushFront(listnode, 2);
	LTPushFront(listnode, 3);
	LTPushFront(listnode, 4);
	LTPushFront(listnode, 5);
	LTPrint(listnode);
	LTPopFront(listnode);
	LTPopFront(listnode);
	LTPopFront(listnode);
	LTPopFront(listnode);
	LTPopFront(listnode);
	LTPrint(listnode);
}

void test2()
{
	LT* listnode = LTIni();
	LTPushBack(listnode, 1);
	LTPushBack(listnode, 2);
	LTPushBack(listnode, 3);
	LTPushBack(listnode, 4);
	LTPushBack(listnode, 5);
	LTPrint(listnode);
	LT* findnode = LTFind(listnode, 5);
	LTInsert(100, findnode);
	LTInsert(100, listnode);
	LTPrint(listnode);
	LTErase1(listnode);
	listnode = NULL;
	LTErase1(findnode);
	findnode = NULL;
	LTPrint(listnode);
	LTErase2(findnode);
	findnode = NULL;
	LTPrint(listnode);
}

void test3()
{
	LT* listnode = LTIni();
	LTPushBack(listnode, 1);
	LTPushBack(listnode, 2);
	LTPushBack(listnode, 3);
	LTPushBack(listnode, 4);
	LTPushBack(listnode, 5);
	//LTErase2(listnode);
	//listnode = NULL;//这样已经避免了不能传入哨兵位。
	LTPrint(listnode);
	LTDestroy(listnode);
	listnode = NULL;
}

int main()
{
	//test1();
	//test2();
	test3();
	return 0;
}

三、链表和顺序表的对比

顺序表链表(主要是双向带头循环链表)
支持下标随机访问, 便于排序下标随机访问不方便,不便于随机排序
空间连续,便于缓存命中
头部,尾部插删效率低,需要挪动数据在知道结点地址的情况下,任意位置的插删都是O(1)
空间不够需要扩容,扩容有一定的消耗且有一定的空间浪费按需申请释放,合理利用空间,不存在浪费。

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

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

相关文章

SAP Debug时如何跳过(不执行)某些代码

Debug时如何跳过(不执行)某些代码 在DEBUG界面, 首先将光标定位到想跳至的代码行, 然后从右键菜单中选择Goto Statement, 或者从Debugger菜单中选择Goto Statement:&#xff08;效果相同&#xff09; 然后光标就会定位到想跳至的代码行 执行结果如下: 结果是000的原因是&#…

保姆级前端翻牌效果(CSS)

效果 翻牌效果 hover 时候 代码直接上 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><title>Document<…

微服务的注册发现和微服务架构下的负载均衡

文章目录 微服务注册模型服务注册与发现怎么保证高可用【1. 服务端崩溃检测】【2. 客户端容错】【3. 注册中心选型】 微服务架构下的负载均衡【1.轮询与加权轮询】【2.随机与加权随机】【3.哈希与一致性哈希】【4.最少连接数】【5.最少活跃数】【6.最快响应时间】【总结】 负载…

消防救援大队应用“码“上监管 实现重点领域监督全覆盖

近年以来&#xff0c;一直存在消防安全风险防控不精准、问题发现不及时、监督效果不明显等难点问题&#xff0c;我们充分利用信息化手段&#xff0c;探索开通“码上监督”网络举报平台&#xff0c;实现监督途径从“线下”拓展到“线上”&#xff0c;“码上监督”马上办。 问题…

容器size()无符号数导致的for循环崩溃

1.问题描述 容器size()无符号数导致的for循环崩溃 for (int index 0; index < static_cast(intVec.size())-1; index) { printf(“%d”,intVec[index]); } 如果不做强转&#xff0c;可能会有两个问题&#xff1a; &#xff08;1&#xff09;编译不过 &#xff08;2&#x…

K8S 集群搭建

1、搭建清单 2台linux服务器&#xff08;一个master节点&#xff0c;一个node节点&#xff09;&#xff0c;建议搭3台&#xff08;一个master&#xff0c;两个node&#xff09; 我使用的是腾讯云&#xff0c;节点与节点使用公网IP通信 确保2台服务器都安装了docker 2、服务…

unity 使用Vuforia扫描实体物体交互

文章目录 前言一、Vuforia是什么&#xff1f;二、Unity导入Vuforia1.去Unity - Windows – Asset Store&#xff0c;搜vuforia engine&#xff0c;添加到我的资源2.从 Unity 的菜单 Assets -> Import package -> Custom Package 导入脚本&#xff0c;添加 Vuforia Engine…

Echarts 图表添加横向 竖向滚动条

1.横向滚动条 dataZoom: [{// 设置滚动条的隐藏与显示show: true,// 设置滚动条类型type: "slider",// 设置背景颜色backgroundColor: "rgb(19, 63, 100)",// 设置选中范围的填充颜色fillerColor: "rgb(16, 171, 198)",// 设置边框颜色borderCo…

PGVector 管理工具 pgAdmin

PGVector 管理工具 pgAdmin pgAdmin 下载地址pgAdmin 安装pgAdmin 使用 pgAdmin 下载地址 https://www.postgresql.org/ftp/pgadmin/pgadmin4/ pgAdmin 安装 双击 pgadmin4-*-x64.exe 安装文件&#xff0c;选择安装路径&#xff0c;后面安装提示单击 next 就可以了。 pgAdm…

数据迁移教程 | 从 Postgre/Greenplum 到 DolphinDB

PostgreSQL 是一种开源的关系型数据库管理系统&#xff08;DBMS&#xff09;&#xff0c;是最广泛使用的开源数据库之一。它允许用户通过添加自定义函数、数据类型和索引等方式扩展其功能&#xff0c;支持 ACID 事务&#xff0c;并使用多版本并发控制 (MVCC&#xff09;来管理并…

JDK 环境变量设置

目录 一. 前言 二. 下载 JDK 2.1. JDK 8 2.2. JDK 17 2.3. JDK 21 三. 环境变量设置 3.1. Windows 环境配置 3.1.1. 打开环境变量配置窗口 3.1.2. 配置环境变量 JAVA_HOME 3.1.3. 配置环境变量 CLASSPATH 3.1.4. 环境变量 Path 末尾追加 3.1.5. 检查JDK是否安装成…

北斗卫星推动我国法治建设

北斗卫星推动我国法治建设发展 10月26日下午&#xff0c;第二届北斗规模应用国际峰会北斗规模应用法治保障专题论坛在湖南省株洲市召开。与会专家围绕北斗法治建设全局、北斗涉外法治建设、北斗品牌塑造、北斗产业生态建设及政策法规完善等方面&#xff0c;进行了深入研讨交流。…

搬砖日记:post传数组(三种格式)

1. json型 request({url: /msg/message/batch/read,data,method: post,content-Type: application/json })2. formData数组型 Content-Type: application/x-www-form-urlencoded request({url: /msg/message/batch/read,data,method: post,})3.formData字段重复传型 把data换成…

经典文献阅读之--Fast and Robust Ground Surface Estimation...(均匀B样条采样快速估计地平面)

0. 简介 对于激光雷达的地面估计分割&#xff0c;目前其实有很多方法做了快速并鲁棒的分割&#xff0c;比如说我们之前写的一篇《经典文献阅读之–FEC》一文中就给出了快速分割的方案&#xff0c;当中第一步就是需要对地面进行分割。而我们这次看的是一篇使用均匀B样条的方法来…

第2关:多表查询

任务描述 join操作符编程要求测试说明 任务描述 本关任务&#xff1a; 使用join操作符实现多表查询。 join操作符 1.笛卡尔积&#xff0c;RXS 可直接转换为SQL语句 2.等值连接&#xff0c;记作 可直接转换为SQL语句 3.自然连接&#xff0c;记作 可转换为SQL语句 4.左外连接…

Java架构核心基础知识硬核整理,赶快收藏起来吧!!!

Java架构核心基础 lecture&#xff1a;波哥 一、数据结构和算法 1.数据结构 数据结构是计算机存储、组织数据的方式。数据结构是指相互之间存在一种或多种特定关系的数据元素的集合。通常情况下&#xff0c;精心选择的数据结构可以带来更高的运行或者存储效率。数据结构往往同…

保护您的Google账号安全:检查和加固措施

简介&#xff1a;随着我们在日常生活中越来越依赖于Google账号&#xff0c;我们的个人信息和敏感数据也变得越来越容易受到威胁。为了确保您的Google账号的安全性&#xff0c;本文将介绍一些简单但有效的方法&#xff0c;帮助您检查和加固您的Google账号。 --- 在数字时代&am…

【工具使用】卸载VS(Visual Studio)

目录 方法一&#xff1a;使用TotalUninstaller工具方法二&#xff1a;官网的卸载方法 方法一&#xff1a;使用TotalUninstaller工具 下载地址&#xff1a;https://github.com/Microsoft/VisualStudioUninstaller/releases 1.点击下载地址&#xff0c;选择TotalUninstaller进行…

CNKI上最新硕士博士论文pdf格式文件owner密码找回难度

听人说CNKI上比较早期的硕士博士论文pdf格式文件密码修改权限Owner密码是123456&#xff0c;想办法找了几个文件试了试果然如此。 但又听人说CNKI上最新硕士博士论文pdf格式文件owner密码已经不是了。虽然直接移除这种密码的工具到处都是&#xff0c;推测一下新增的owner密码及…

从道一云到畅捷通T+通过接口配置打通数据

从道一云到畅捷通T通过接口配置打通数据 接通系统&#xff1a;道一云 在道一云坚实的技术基础上&#xff0c;道一云推出全新升级的2.0产品矩阵&#xff0c;分别是低码平台、智能门户、场景应用。基于云原生底座&#xff0c;为企业提供集智能门户解决网关流量问题、企业微信端的…