【数据结构】带头节点双向循环链表

news2025/1/12 15:45:06

目录

顺序表和链表的区别

带头双向循环链表分析

带头双向循环链表结构:

 创建一个节点

哨兵位头节点

打印链表中的值

在pos前插入

删除pos位置的节点

尾插

尾删

头插:

头删

链表元素查找

总代码

 List.h文件

List.c文件

test.c文件


顺序表和链表的区别

 

        顺序表:

优点:尾插尾删效率高,下标的随机访问快。

缺点:空间不够需要扩容(扩容空间大);头部或中间插入删除效率低,需要挪动数据。

        链表:

优点:需要扩容,按需申请释放大小快节点内存;任意位置插入效率高——O(1).

缺点:不支持下标随机访问。

链表类型有8种:

 

 虽然有这么多的链表结构,但是实际中最常用的还是如下图两种结构:

1、无头单向非循环链表:结构简单,一般不会单独用来存数据。实际中更多的是作为其他数据结构的子结构,如哈希表、图的邻接表等。这两种结构在笔试面试种出现很多。

2、带头双向循环链表:结构最复杂,一般用在单独存储数据。实际中使用的链表数据结构,都是带头双向循环链表。另外这个结构虽然复杂,但是使用代码实现后以后会发现结构带来了很多优势,实现反而简单了。

带头双向循环链表分析

带头双向循环链表结构:

typedef int LTDataType;
typedef struct ListNode
{
    struct ListNode *prev;
    srtuct ListNode *next;
    LTDataType data;
}LTNode;

 

 创建一个节点

LTNode *BuyListNode(LTDataType x)
{
	LTNode* newnode = (LTNode*)malloc(sizeof(LTNode));
	if (newnode == NULL)
	{
		perror("malloc fail");
		exit(-1);
	}
	newnode->prev = NULL;
	newnode->next = NULL;
	newnode->data = x;
	return newnode;
}

哨兵位头节点

LTNode* LTInit()                     //头节点(哨兵位)
{
	LTNode* phead = BuyListNode(-1);
	phead->next = phead;
	phead->prev= phead;
	return phead;
}

打印链表中的值

void LTPrint(LTNode*phead)
{
	assert(phead);
	LTNode* cur = phead->next;
	while (cur != phead)
	{
		printf("%d ", cur->data);
		cur = cur->next;
	}
	printf("\n");
}

这里需要注意一下打印结束的标志,单链表的打印结束标志位最后一个节点为空,即while(cur);而带头双向循环链表的打印结束标志是最后一个节点指向哨兵位节点,即while(cur!= phead)。

在pos前插入

在pos位置之前插入值时,注意先将pos的前一个节点的地址记下来,再插入值:

 

void LTInsert(LTNode* pos, LTDataType x)
{
	assert(pos);
	LTNode* prev = pos->prev;
	LTNode* newnode = BuyListNode(x);

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

删除pos位置的节点

 删除pos位置的节点之前可以将pos的前一个节点和pos后一个节点的地址记下来:

 

 

//删除pos位置
void LTErase(LTNode* pos)
{
	assert(pos);
	LTNode* prev = pos->prev;
	LTNode* next = pos->next;

	prev->next = next;
	next->prev = prev;
	free(pos);
}

尾插

相当于在phead前面插入一个值,而哨兵位节点的地址没有改变。在带哨兵位双向循环链表中,哨兵位的前一个节点指向的地址(phead->prev)相当于最后一个,因为此链表中第一个节点的地址是phead->next:

void LTPushBack(LTNode*phead,LTDataType x)
{
	assert(phead);
	/*LTNode* newnode = BuyListNode(x);
	LTNode* tail = phead->prev;

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

	newnode->next = phead;
	phead->prev = newnode;*/
	LTInsert(phead, x);//相当于在phead之前插入一个节点,此时的哨兵节点的位置仍然指向phead,phead->pre为尾节点(双链表)

}

尾删

相当于将哨兵位前一个节点(phead->prev)给free掉:

void LTPopBack(LTNode* phead)
{
	assert(phead);
	assert(phead->next != NULL);  // 哨兵位的下一位不为空
/*
LTNode* tail = phead->prev;
	LTNode* tailprev = tail->prev;

	phead->prev = tailprev;
	tailprev->next = phead;
	free(tail);

*/
	LTErase(phead->prev);
}
	

头插:

相当于在哨兵位后一个节点之前插入:

 

void LTPushFront(LTNode* phead, LTDataType x)
{
	assert(phead);
/*
LTNode* newnode = BuyListNode(x);
	LTNode*first = phead->next;           //first记住phead->next的地址;

	newnode->next = first;
	newnode->prev = phead;
	first->prev = newnode;

	phead->next = newnode;
*/
	LTInsert(phead->next, x);
}

头删

删除哨兵位(phead)的后一个节点(phead->next):

 

void LTPopFront(LTNode* phead)
{
	assert(phead);
	assert(phead->next != NULL);//哨兵位的下一位即第一个节点不为空
/*
	LTNode* first = phead->next;//first记住头节点地址
	LTNode* second = first->next;

	phead->next = second;
	second->prev = phead;
	free(first);

*/
	LTErase(phead->next);
}

注释掉的代码也是可以运行的,相当于LTErase(phead->next) 

链表元素查找

如果一直查找,直到找到哨兵位节点还没找到,则返回NULL;

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

总代码

 List.h文件

在此声明函数和定义链表的结构体

#pragma once
#include<stdio.h>
#include<assert.h>
#include<stdlib.h>
#include<stdbool.h>

typedef int LTDataType;
typedef struct ListNode
{
	struct ListNode* next;           //后一个节点地址
	struct ListNode* prev;          //前一个节点地址
	LTDataType data;
}LTNode;

LTNode* BuyListNode(LTDataType x);//新建一个节点
LTNode* LTInit();

void LTPrint(LTNode* phead);                 //打印
void LTPushBack(LTNode* phead, LTDataType x);//尾插
void LTPopBack(LTNode* phead);               //尾删

void LTPushFront(LTNode* phead, LTDataType x);//头插
void LTPopFront(LTNode* phead);

LTNode* LTFind(LTNode* phead, LTDataType x);//查找双链表的元素

//pos插入x
void LTInsert(LTNode* pos, LTDataType x);
//pos删除
void LTErase(LTNode* pos);

bool LTEmpty(LTNode* phead);
size_t LTSize(LTNode* phead);    //双链表长度
void LTDestroy(LTNode* phead);

List.c文件

在此定义函数

#define  _CRT_SECURE_NO_WARNINGS
#include"List.h"

LTNode *BuyListNode(LTDataType x)
{
	LTNode* newnode = (LTNode*)malloc(sizeof(LTNode));
	if (newnode == NULL)
	{
		perror("malloc fail");
		exit(-1);
	}
	newnode->prev = NULL;
	newnode->next = NULL;
	newnode->data = x;
	return newnode;
}

LTNode* LTInit()                     //头节点(哨兵位)
{
	LTNode* phead = BuyListNode(-1);
	phead->next = phead;
	phead->prev= phead;
	return phead;
}

void LTPrint(LTNode*phead)
{
	assert(phead);
	LTNode* cur = phead->next;
	while (cur != phead)
	{
		printf("%d ", cur->data);
		cur = cur->next;
	}
	printf("\n");
}

void LTPushBack(LTNode*phead,LTDataType x)
{
	assert(phead);
	/*LTNode* newnode = BuyListNode(x);
	LTNode* tail = phead->prev;

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

	newnode->next = phead;
	phead->prev = newnode;*/
	LTInsert(phead, x);//相当于在phead之前插入一个节点,此时的哨兵节点的位置仍然指向phead,phead->pre为尾节点(双链表)

}

void LTPopBack(LTNode* phead)
{
	assert(phead);
	assert(phead->next != NULL);  // 哨兵位的下一位不为空
/*
LTNode* tail = phead->prev;
	LTNode* tailprev = tail->prev;

	phead->prev = tailprev;
	tailprev->next = phead;
	free(tail);

*/
	LTErase(phead->prev);
}
	

void LTPushFront(LTNode* phead, LTDataType x)
{
	assert(phead);
/*
LTNode* newnode = BuyListNode(x);
	LTNode*first = phead->next;           //first记住phead->next的地址;

	newnode->next = first;
	newnode->prev = phead;
	first->prev = newnode;

	phead->next = newnode;
*/
	LTInsert(phead->next, x);
}

void LTPopFront(LTNode* phead)
{
	assert(phead);
	assert(phead->next != NULL);//哨兵位的下一位即第一个节点不为空
/*
	LTNode* first = phead->next;//first记住头节点地址
	LTNode* second = first->next;

	phead->next = second;
	second->prev = phead;
	free(first);

*/
	LTErase(phead->next);
}
//在pos之前插入x
void LTInsert(LTNode* pos, LTDataType x)
{
	assert(pos);
	LTNode* prev = pos->prev;
	LTNode* newnode = BuyListNode(x);

	prev->next = newnode;
	newnode->prev = prev;
	newnode->next = pos;
	pos->prev = newnode;
}
//删除pos位置
void LTErase(LTNode* pos)
{
	assert(pos);
	LTNode* prev = pos->prev;
	LTNode* next = pos->next;

	prev->next = next;
	next->prev = prev;
	free(pos);
}

bool LTEmpty(LTNode* phead)
{
	assert(phead);
	/*
	if(phead->next==phead)
	{
		return true;
	}
	else
	{
		return false;
	}
	*/
	return phead->next == phead;
}

size_t LTSize(LTNode* phead)
{
	assert(phead);

	size_t size = 0;
	LTNode* cur = phead->next;
	while (cur != phead)
	{
		++size;
		cur = cur->next;
	}
	return size;
}

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

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

test.c文件

在此测试和放主函数

#define  _CRT_SECURE_NO_WARNINGS
#include"List.h"
void testLTNode1()
{
	LTNode* phead = LTInit();
	LTPushBack(phead, 1);
	LTPushBack(phead, 2);
	LTPushBack(phead, 2);
	LTPushBack(phead, 3);
	LTPushBack(phead, 4);
	LTPrint(phead);

	LTPopBack(phead);
	LTPopBack(phead);
	LTPopBack(phead);
	LTPrint(phead);

	LTPushFront(phead, 1);
	LTPushFront(phead, 2);
	LTPushFront(phead, 3);
	LTPushFront(phead, 4);
	LTPrint(phead);

	LTPopFront(phead);
	LTPopFront(phead);
	LTPrint(phead);
}

void testLTNode2()
{
	LTNode* phead = LTInit();
	LTPushFront(phead, 1);
	LTPushFront(phead, 2);
	LTPushFront(phead, 3);
	LTPushFront(phead, 4);
	LTPrint(phead);
	LTNode* pos = LTFind(phead, 3);
	if (pos)
	{
		pos->data *= 10;
	}
	LTPrint(phead);

	pos = LTFind(phead, 30);
	LTErase(pos);
	LTPrint(phead);

	pos = LTFind(phead, 4);
	LTInsert(pos, 3);
	LTPrint(phead);

	pos = NULL;
	LTDestroy(phead);
	phead = NULL;
}

int main()
{
	//testLTNode1();
	testLTNode2();
	return 0;
}

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

科技云报道:历经四年,RPA走向同质化?

科技云报道原创。 经过多年发展&#xff0c;全球RPA市场已经初具规模。 据Transparency Market Research研究预测&#xff0c;预计到2024年&#xff0c;全球RPA市场规模将达到50亿美元&#xff0c;实现61.3%的年复合增长率。 RPA在亚洲市场起步晚于欧美市场&#xff0c;但从2…

SpringCloud系列(二)Ribbon 负载均衡的原理及详细流程

关于负载均衡这个概念在上一篇文章中有所提到&#xff0c;在消费者远程调用之前有一个重要的环节就是负载均衡&#xff0c;那么为什么要进行负载均衡呢&#xff1f;其原理及实现流程如何&#xff1f;   其实 Ribbon 负载均衡可以认为是一种策略&#xff0c;也可以说是某种规则…

SpringBoot+Vue项目实现身体健康诊疗系统

文末获取源码 开发语言&#xff1a;Java 使用框架&#xff1a;spring boot 前端技术&#xff1a;JavaScript、Vue.js 、css3 开发工具&#xff1a;IDEA/MyEclipse/Eclipse、Visual Studio Code 数据库&#xff1a;MySQL 5.7/8.0 数据库管理工具&#xff1a;phpstudy/Navicat JD…

ajax尚硅谷笔记——跨域请求、axios发送ajax请求、jquery发送ajax请求

去恶补了ajax知识 一、ajax简介 1、ajax全称为Asynchronous JavaScript And XML&#xff0c;就是异步的JS 和XML 2、通过AJAX可以再浏览器中向服务器发送异步请求&#xff0c;最大的优势&#xff1a;无刷新获取数据 3、ajax不是新的编程语言&#xff0c;而是一种将现有的标准…

《Linux内核设计与实现》读书笔记

《Linux内核设计与实现》读书笔记第三章 进程管理第四章 进程调度第五章 系统调用第六章 内核数据结构第七章 中断和中断处理第八章 下半部和推后执行的工作第九章 内核同步介绍第十章 内核同步方法第十一章 定时器和时间管理第十二章 内存管理第十三章 虚拟文件系统第十四章 块…

Java:2022年全球使用的15种最流行的Java应用

到今年为止&#xff0c;Java已经有25年的历史了&#xff0c;尽管引入了许多更新、更华丽的语言和工具&#xff0c;但它仍然是当今最流行的编程语言之一。这们老语言一直在蹒跚前行&#xff0c;享受着当今众多程序员和开发人员的爱。 Java有许多优势&#xff0c;再加上它的广泛使…

transformer论文及其变种

文章目录transformer模型细节slf-attn & multi-head attnabs positionwhy slf-attntransformer-XLInformer细节probSparse slf-attnLongformer细节GPT-generative pre-train模型结构下游任务&#xff1a;fine-tuningtransformer motivation&#xff1a;序列映射的任务&…

高速串行信号串接电容放在发送端还是接收端

在设计一些高速的串行信号&#xff0c;比如PCIE&#xff0c;STATA&#xff0c;USB3.0等&#xff0c;在差分信号线上面常常都会串接一个电容 这个电容主要有如下几个方面的作用&#xff1a; 1.滤除信号的直流分量&#xff0c;使信号关于0电平对称&#xff1b; 因为很多高速信号…

持续集成环境-maven、tomcat安装和配置

在Jenkins 集成环境中&#xff0c;用Maven编译、打包项目 壹&#xff0c;安装Maven 安装在jenkins服务器上 官方下载地址 上传安装包 解压 &#xff1a; tar -zxvf apache-maven-3.6.2-bin.tar.gzmkdir -p /opt/maven #创建目录 mv apache-maven-3.6.2/* /opt/maven #移…

Vue3中v-if与v-for、多事件处理器即案件修饰符、$attrs、$root和$parent

文章目录1. v-if与v-for及动态属性ref的使用2. 多事件处理器及按键修饰符3. $attrs包含class和style4. \$root和$parent1. v-if与v-for及动态属性ref的使用 在 vue3 中&#xff0c;当 v-if 与 v-for 一起使用时&#xff0c;v-if 具有比 v-for 更高的优先级。 下面是 v-for 结…

ubuntu20.04搭建janus服务器

目录 一、安装依赖项 二、编译janus v1.1.0 三、生成ssl证书 四、编译配置nginx 五、编译turnserver 六、配置janus文件 七、编译janus报错记录 参考资料&#xff1a; 环境是ubuntu20.04 使用最新的janus v1.1.0代码。 一、安装依赖项 sudo apt-get install aptitude…

Linux搭建Rabbitmq集群

1.1 添加其他用户 133、134、135 因为 guest 用户只能在本机访问&#xff0c;添加一个 admin 用户&#xff0c;密码也是 admin ./rabbitmqctl add_user admin admin ./rabbitmqctl set_user_tags admin administrator ./rabbitmqctl set_permissions -p / admin “." &qu…

嵌入式软件设计之美-以实际项目应用MVC框架与状态模式(下)

上节我们分享了MVC框架、状态模式组合在实际开发中的应用&#xff0c;它能够让我们的软件设计流程更加的清晰、易于维护&#xff1a; 嵌入式软件设计之美-以实际项目应用MVC框架与状态模式(上) 那么这一节我们就直接开门见山&#xff0c;从接下来的这个开源项目分享开始&…

An2023(Animate2023)中文版软件下载「附带安装教程」

animate2023版本已经更新&#xff0c;此次的最新版本中&#xff0c;拥有大量的新特性&#xff0c;特别是在继续支持Flash SWF、AIR格式的同时&#xff0c;还会支持HTML5Canvas、WebGL&#xff0c;并能通过可扩展架构去支持包括SVG在内的几乎任何动画格式&#xff0c;更新推出了…

MySQL数据库期末考试试题及参考答案(06)

版权声明 本文原创作者&#xff1a;谷哥的小弟作者博客地址&#xff1a;http://blog.csdn.net/lfdfhl 一、 填空题 普通索引使用KEY或____定义。在MySQL中&#xff0c;DROP VIEW语句用于____。MySQL中常见的索引大致分为普通索引、 ____ 、 ____ 、全文索引、空间索引。只有在…

《STL源码剖析》笔记——allocator

六大组件间关系 部分STL文件包含关系 allocator包含于中&#xff1a; 实际实现于三个文件 &#xff1a; 1.stl_construct.h :对象的构造和析构 2.stl_alloc.h空间配置和释放 3.stl_uninitialized.h 空间配置器&#xff08;allocator&#xff09; 1.什么是空间配置器&#xff…

MindFusion JS Chart 2.0 Crack

一个用于图表、仪表和仪表板的库。MindFusion JS Chart 结合了 2D 和 3D 图表、财务图表、仪表和仪表板。优雅的 API、丰富的事件集、无限数量和类型的数据系列以及您在JavaScript和HTML中创建完美数据可视化可能需要的一切。 特征 常见图表类型 创建交互式线图、 面积图、 气泡…

深入浅出scala之变量定义(P11-15)

文章目录1. 变量定义2.数据类型3.数值类型4.浮点类型1. 变量定义 基本语法: 变量定义一定要初始化 var|val变量名[: 变量类型] .变量值使用var或者val定义一个变量。 使用var(variable)声明变量&#xff0c;可以被重新赋值。 //定义变量a2,手动指定类型为Int,此时需要保证所…

如何完成视频合并操作?这几个方法值得一试

我们在拍摄视频vlog的时候&#xff0c;不可能持续、完整地拍一整天&#xff0c;而是通过很多视频素材中&#xff0c;挑选几段有意义的部分&#xff0c;将他们剪辑出来&#xff0c;然后再进行合并。大家可能会觉得视频剪辑很难学&#xff0c;其实不然&#xff0c;我们借助一些专…

[附源码]java毕业设计小说网站的设计与实现1

项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; SSM mybatis Maven Vue 等等组成&#xff0c;B/S模式 M…