数据结构——带头双向循环链表

news2024/11/25 7:25:16

数据结构——带头双向循环链表

  • 一、带头双向循环链表的定义
  • 二、带头双向循环链表的实现
    • 2.1初始化创建带头双向循环链表的节点
    • 2.2申请新节点
    • 2.3节点的初始化
    • 2.4带头双向循环链表的尾插
    • 2.5带头双向循环链表的头插
    • 2.6判空函数
    • 2.7带头双向循环链表的打印函数
    • 2.8带头双向循环链表的尾删
    • 2.9带头双向循环链表的头删
    • 2.11带头双向循环链表的在pos之前插入
    • 2.12带头双向循环链表的在pos位置删除
    • 2.14带头双向循环链表的销毁
  • 三、完整代码
    • 3.1LIst.h
    • 3.2List.c
    • 3.3test.c

一、带头双向循环链表的定义

带头双向循环链表:结构最复杂,一般用在单独存储数据。实际中使用的链表数据结构,都是带头双向循环链表。另外这个结构虽然结构复杂,但是使用代码实现以后会发现结构会带来很多优势。
带头双向循环链表包括一个带有哨兵位的头节点,该节点既可以作为链表的第一个节点,也可以作为链表的最后一个节点.
这种链表的特点是每个节点都有两个指针,一个指向前一个节点,一个指向后一个节点,这样就可以实现双向遍历。
同时,链表的最后一个节点的后继指针指向头节点,形成了循环的结构。这样,我们可以在任意一个节点上进行前后移动,插入和删除操作,而不需要像单链表那样遍历整个链表去找到前一个节点。
需要注意的是,带头双向循环链表为空并不意味着没有一个节点,而是只有一个带哨兵位的头节点,所以在使用之前需要对链表进行初始化。

二、带头双向循环链表的实现

2.1初始化创建带头双向循环链表的节点

typedef struct ListNode
{
	Listdatatype data;
	struct ListNode* next;
	struct ListNode* prev;
}LTNode;

带头双向循环链表示意图

在创建带头双向循环链表的节点中比之前单链表节点的创建多了一个struct ListNode* prev;结构体指针,目的在与存储前一个节点的地址,便于将整个链表连在一起。

2.2申请新节点

//创建新节点
LTNode* BuyLTNode(Listdatatype x)
{
	LTNode* newnode = (LTNode*)malloc(sizeof(LTNode));
	if (newnode == NULL)
	{
		perror("malloc fail");
		return NULL;
	}
	newnode->data = x;
	newnode->next = NULL;
	newnode->prev = NULL;
	return newnode;
}

动态申请内存结点,函数返回的是一个指针类型,用malloc开辟一个LTNode大小的空间,并用node指向这个空间,再判断是否为空,如为空就perror,显示错误信息。反之则把需要存储的数据x存到newnode指向的空间里面,并且把newnode->next,newnode->prev置为空。

2.3节点的初始化

LTNode* LTInit()
{
	LTNode* phead = BuyLTNode(-1);
	phead->next = phead;
	phead->prev = phead;

	return phead;
}

通过动态内存申请节点,申请了一个头节点。并且将它的phead->next ,phead->prev 都置为phead,得到如下图的头节点。
哨兵位的头节点

2.4带头双向循环链表的尾插

void LTPushBack(LTNode* phead, Listdatatype x)
{
	assert(phead);
	LTNode* tail = phead->prev;
	LTNode* newnode = BuyLTNode(x);

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

尾插节点的方法:首先通过内存申请一个节点, 然后改变四个指针的指向,便可以完成带头双向循环链表的尾插。
在这里插入图片描述
在这里插入图片描述

2.5带头双向循环链表的头插

void LTFrontBack(LTNode* phead, Listdatatype x)
{
	assert(phead);
	LTNode* newnode = BuyLTNode(x);
	newnode->next = phead->next;
	phead->next->prev = newnode;
	phead->next = newnode;
	newnode->prev = phead;
}

在这里插入图片描述
在这里插入图片描述

2.6判空函数

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

	return phead->next == phead;
}

2.7带头双向循环链表的打印函数

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

2.8带头双向循环链表的尾删

//尾删
void LTPopBack(LTNode* phead)
{
	assert(phead);
	assert(! LTEmpty(phead));
	LTNode* tail = phead->prev;
	LTNode* tailprev = tail->prev;
	//改变指针的指向
	free(tail);
	tailprev->next = phead;
	phead->prev = tailprev;
}

在这里插入图片描述

2.9带头双向循环链表的头删

void LTPopFront(LTNode* phead)
{
	assert(phead);
	assert(!LTEmpty(phead));
	LTNode* first = phead->next;
	LTNode* firstnext = first->next;
	free(first);
	phead->next = firstnext;
	firstnext->prev = phead;
}

在这里插入图片描述

2.11带头双向循环链表的在pos之前插入

void LTInsert(LTNode* pos, Listdatatype x)
{
	assert(pos);
	LTNode* newnode = BuyLTNode(x);
	LTNode* posprev = pos->prev;
	posprev->next = newnode;
	newnode->prev = posprev;
	newnode->next = pos;
	pos->prev = newnode;
}

在这里插入图片描述

2.12带头双向循环链表的在pos位置删除

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

在这里插入图片描述

2.14带头双向循环链表的销毁

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

三、完整代码

3.1LIst.h

#pragma once
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
#include<stdbool.h>
typedef int  Listdatatype;
typedef struct ListNode
{
	Listdatatype data;
	struct ListNode* next;
	struct ListNode* prev;
}LTNode;

//初始化
LTNode* LTInit();

//尾插
void LTPushBack(LTNode* phead, Listdatatype x);

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

//头插
void LTFrontBack(LTNode* phead, Listdatatype x);

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

//打印
void LTPrint(LTNode* phead);

//判空
bool LTEmpty(LTNode* phead);

//在pos之前插入
void LTInsert(LTNode* pos, Listdatatype x);

//在pos之前删除
void LTErase(LTNode* pos);

//寻找
LTNode* LTFind(LTNode* phead, Listdatatype x);

//销毁
LTNode* LTDestory(LTNode* phead);

3.2List.c

#define _CRT_SECURE_NO_WARNINGS 1
#include"List.h"
//创建新节点
LTNode* BuyLTNode(Listdatatype x)
{
	LTNode* newnode = (LTNode*)malloc(sizeof(LTNode));
	if (newnode == NULL)
	{
		perror("malloc fail");
		return NULL;
	}
	newnode->data = x;
	newnode->next = NULL;
	newnode->prev = NULL;
	return newnode;
}


LTNode* LTInit()
{
	LTNode* phead = BuyLTNode(-1);
	phead->next = phead;
	phead->prev = phead;

	return phead;
}
//尾插
void LTPushBack(LTNode* phead, Listdatatype x)
{
	assert(phead);
	LTNode* tail = phead->prev;
	LTNode* newnode = BuyLTNode(x);

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


//头插
void LTFrontBack(LTNode* phead, Listdatatype x)
{
	assert(phead);
	LTNode* newnode = BuyLTNode(x);
	newnode->next = phead->next;
	phead->next->prev = newnode;
	phead->next = newnode;
	newnode->prev = phead;
}

//判空
bool LTEmpty(LTNode* phead)
{
	assert(phead);

	return phead->next == phead;
}

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


//尾删
void LTPopBack(LTNode* phead)
{
	assert(phead);
	assert(! LTEmpty(phead));
	LTNode* tail = phead->prev;
	LTNode* tailprev = tail->prev;
	//改变指针的指向
	free(tail);
	tailprev->next = phead;
	phead->prev = tailprev;
}


//头删
void LTPopFront(LTNode* phead)
{
	assert(phead);
	assert(!LTEmpty(phead));
	LTNode* first = phead->next;
	LTNode* firstnext = first->next;
	free(first);
	phead->next = firstnext;
	firstnext->prev = phead;
}


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



//在pos之前插入
void LTInsert(LTNode* pos, Listdatatype x)
{
	assert(pos);
	LTNode* newnode = BuyLTNode(x);
	LTNode* posprev = pos->prev;
	posprev->next = newnode;
	newnode->prev = posprev;
	newnode->next = pos;
	pos->prev = newnode;
}

在pos之前删除
//void LTErase(LTNode* pos)
//{
//	assert(pos);
//	LTNode* posprev = pos->prev;
//	free(posprev);
//	posprev->prev->next = pos;
//	pos->prev = posprev->prev;
//	
//}


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

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

3.3test.c

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

//}
void test2()
{
	LTNode* plist = LTInit();
	LTFrontBack(plist, 1);
	LTFrontBack(plist, 2);
	LTFrontBack(plist, 3);
	LTFrontBack(plist, 4);
	LTPrint(plist);
	LTErase(3);
	LTPrint(plist);
}
int main()
{
	//test1();
	test2();
	return 0;
}

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

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

相关文章

计算机竞赛 基于深度学习的目标检测算法

文章目录 1 简介2 目标检测概念3 目标分类、定位、检测示例4 传统目标检测5 两类目标检测算法5.1 相关研究5.1.1 选择性搜索5.1.2 OverFeat 5.2 基于区域提名的方法5.2.1 R-CNN5.2.2 SPP-net5.2.3 Fast R-CNN 5.3 端到端的方法YOLOSSD 6 人体检测结果7 最后 1 简介 &#x1f5…

OpenCV 04(通道分离与合并 | 绘制图形)

一、通道的分离与合并 - split(mat)分割图像的通道 - merge((ch1,ch2, ch3)) 融合多个通道 import cv2 import numpy as npimg np.zeros((480, 640, 3), np.uint8)b,g,r cv2.split(img)b[10:100, 10:100] 255 g[10:100, 10:100] 255img2 cv2.merge((b, g, r))cv2.imshow…

《TCP/IP网络编程》阅读笔记--并发多进程服务端的使用

1--并发服务器端 并发服务器端主要有以下三类&#xff1a; ① 多进程服务器&#xff1a;通过创建多个进程提供服务&#xff1b; ② 多路复用服务器&#xff1a;通过捆绑并统一管理I/O对象提供服务&#xff1b; ③ 多线程服务器&#xff1a;通过生成与客户端等量的线程提供服务&…

C/C++ ——内存管理

前言 为什么要研究内存管理&#xff1f; (1)程序员写的程序可以分为动态和静态两种状态。静态&#xff1a;就是程序被存放在ROM中&#xff0c;也就是磁盘、固态硬盘、eMMC等存储介质&#xff1b;动态&#xff1a;程序被执行&#xff0c;此时程序在RAM内存中运行&#xff1b; (…

图床项目数据库表设计

一、表设计 share_picture_list 和 share_file_list 类似&#xff0c;只是 share_picture_list 只存储共享图片相关的信息&#xff0c;及分享给未注册用户看的。share_file_list 是存储共享文件&#xff08;包括图片文件&#xff09;相关的信息&#xff0c;分享给已注册用户的。…

【数据结构】 七大排序详解(贰)——冒泡排序、快速排序、归并排序

文章目录 ⚽冒泡排序⚾算法步骤&#x1f3a8;算法优化&#x1f94e;代码实现&#xff1a;&#x1f3c0;冒泡排序的特性总结 &#x1f9ed;快速排序⚽算法思路&#x1f4cc;思路一&#xff08;Hoare版&#xff09;&#x1f4cc;思路二&#xff08;挖坑法&#xff09;&#x1f4c…

PCL入门(四):kdtree简单介绍和使用

目录 1. kd树的意义2. kd树的使用 参考博客《欧式聚类&#xff08;KD-Tree&#xff09;详解&#xff0c;保姆级教程》和《(三分钟)学会kd-tree 激光SLAM点云搜索常见》 1. kd树的意义 kd树是什么&#xff1f; kd树是一种空间划分的数据结构&#xff0c;对于多个维度的数据&a…

小米汽车,能否在新能源汽车江湖站稳脚跟?

最近&#xff0c;圈内都在传小米汽车亦庄工厂已试生产近一个月&#xff0c;每周可产50辆样车&#xff0c;正在为首款新能源汽车量产做最后的准备。 此前的业绩交流会上&#xff0c;小米集团总裁卢伟冰透露&#xff0c;小米汽车结束了夏测且进展非常顺利&#xff0c;2024年上半…

计算机竞赛 基于深度学习的植物识别算法 - cnn opencv python

文章目录 0 前言1 课题背景2 具体实现3 数据收集和处理3 MobileNetV2网络4 损失函数softmax 交叉熵4.1 softmax函数4.2 交叉熵损失函数 5 优化器SGD6 最后 0 前言 &#x1f525; 优质竞赛项目系列&#xff0c;今天要分享的是 &#x1f6a9; **基于深度学习的植物识别算法 ** …

数仓学习之DWD学习

登录不是原子行为&#xff0c;而登录成功是&#xff0c; 而支付也不是原子&#xff0c;也分成功失败。 什么是原子型? 一条线 注意一下密码, mysql的密码是MD5加密,而MD5的长度为固定的32 1.怎么构建事务表? 1.确定表名 2.确定一行数据所表示的含义 3.确定列定义 4.确定度…

【算法训练笔记】栈的OJ题

&#x1f525;&#x1f525; 欢迎来到小林的博客&#xff01;&#xff01;       &#x1f6f0;️博客主页&#xff1a;✈️林 子       &#x1f6f0;️博客专栏&#xff1a;✈️ 小林的算法训练笔记       &#x1f6f0;️社区 :✈️ 进步学堂       …

rsa加密解密java和C#互通

前言 因为第三方项目是java的案例&#xff0c;但是原来的项目使用的是java&#xff0c;故需要将java代码转化为C#代码&#xff0c;其中核心代码就是RSA加密以及加签和验签&#xff0c;其他的都是api接口请求难度不大。 遇到的问题 java和c#密钥格式不一致&#xff0c;java使…

C++QT day3

1> 自行封装一个栈的类&#xff0c;包含私有成员属性&#xff1a;栈的数组、记录栈顶的变量 成员函数完成&#xff1a;构造函数、析构函数、拷贝构造函数、入栈、出栈、清空栈、判空、判满、获取栈顶元素、求栈的大小 2> 自行封装一个循环顺序队列的类&#xff0c;包含…

backward问题记录

大致问题是这样的 模型先计算一个输出 然后根据这个输出&#xff0c;用lbfgs去优化另一个变量 最后优化模型&#xff0c;大致代码如下 optimizer optim.Adam(model.parameters(), lrlr)for inputs in dataloader:outputs model(inputs)u outputs.reshape(1, -1, 1)beta to…

扫地机器人还能创新吗?云鲸给了个Yes

作者 | 辰纹 来源 | 洞见新研社 1996年&#xff0c;瑞典家电巨头伊莱克斯推出全球首款扫地机器人“三叶虫”。 与现在的产品相比&#xff0c;“三叶虫”靠随机碰撞的模式对空间进行清扫&#xff0c;清洁效率很低&#xff0c;市场渗透率也不高&#xff0c;但并不妨碍戴森、iRo…

表单引擎的自定义控件的概念与设计

基本概念 概述 控件的定义&#xff1a;用于展示或者采集数据的表单元素&#xff0c;称为控件,比如&#xff1a;文本框、下拉框、单选按钮、从表等.自定义控件&#xff1a;表单引擎提供的基础控件之外的控件称为自定义控件, 这些控件由开发人员自己定义&#xff0c;比如&#…

更灵活的 serverless framework 配置文件

更灵活的 serverless framework 配置文件 前言 再经过前置教程的部署之后&#xff0c;不知道你有没有注意这样一个问题&#xff0c;就是我们部署的函数名&#xff0c;以及 API网关 的 endpoint&#xff0c;它们的名称和路径都带一个 dev? 这个就是 stage 导致的了&#xff…

美客多选品趋势分析,美客多选品时的注意事项

都知道选品的重要性&#xff0c;美客多这个平台也一样&#xff0c;选品选对了肯定事半功倍&#xff0c;本文介绍了美客多选品趋势分析&#xff0c;美客多选品时的注意事项&#xff0c;一起来了解下吧。、 美客多选品趋势分析 1、墨西哥站&#xff1a;跨境支付高&#xff0c;偏…

ESP32C3 PWM输出

目前对于遥控双发差速小飞机计划采用如下架构&#xff1a; ESP32C3做主控&#xff0c;兼具遥控收发和飞行控制锂电池供电&#xff0c;带电量检测双发&#xff0c;720空心杯电机&#xff0c;55mm桨&#xff0c;带电流检测MPU6050加速度计和陀螺仪预留4个控制信号输出 马达控制要…

(Java)多线程带来的的风险-线程安全 (第一部)

前言&#xff1a;线程安全是整个多线程中&#xff0c;最为复杂的部分&#xff0c;也是最重要的部分。 目录 什么是线程安全问题&#xff1f; 线程不安全的原因 ⁜⁜总结 &#xff1a;线程安全问题的原因 ⁜⁜ 解决方法1 ——加锁 synchronized &#xff08;监视器锁monitor…