数据结构之带头双向链表(易学版)

news2024/10/5 18:26:35

目录

1.问题引入

2.结构实现

2.3.1接口实现

2.3.2函数实现

3.总结



,又和大家见面了,今天要给大家分享的是双链表的知识,跟着我的脚步,包学包会哦~

规矩不乱,先赞后看!

ps:(孙权劝学)

1.问题引入

前期学习了单链表,我们尝到了它的甜头,但是容易越界访问这一个问题也是时有出现的,因此也是相对比较棘手的,为了解决这一个问题,特此向大家介绍带头双向链表

带头双向链表,顾名思义含有哨兵位,同时一个节点有两个指针(next / prev),这样的好处是让相邻指针的联系更加紧密,同时将首尾节点相连是其能够非常容易实现找尾。

话不多说,直接上代码!

2.结构实现

2.3.1接口实现

先在头文件(List.h)上进行顺序表结构的创建和对各函数的声明,目的是为了把各部分区分开,使程序便于理解,能清楚的看到各部分对于的作用和功能。

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


typedef int LTDataType;
typedef struct ListNode
{
	struct ListNode* next;
	struct ListNode* prev;

	LTDataType data;

}LTNode;

bool LTEmpty(LTNode* phead);
void LTPushBack(LTNode* phead, LTDataType x);
LTNode* LTInit();
void LTPopFront(LTNode* phead);
void PushFront(LTNode* phead, LTDataType x);
void LTPrint(LTNode* phead);
LTNode* BuyLTNode(LTDataType x);
void LTPopBack(LTNode* phead);
LTNode* LTFind(LTNode* phead, LTDataType x);
void LTInsert(LTNode* pos, LTDataType x);
void LTErase(LTNode* pos);
void LTDestroy(LTNode* phead);

2.3.2函数实现

接着下来是单链表各函数的函数部分,我们在List.c中完成:

a.创造新节点

LTNode* BuyLTNode(LTDataType 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;
}

这些步骤都是和链表一模一样的。

b.初始化链表

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

	return phead;
}

都是链表的一套固定公式

c.查找链表

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;

}

d.在链表中插入节点

由于有了双链表,使得插入十分轻松,同时这一个函数就能简化头插和尾插两个函数,是相当方便的

//在pos前插入
void LTInsert(LTNode* pos, LTDataType x)
{
	assert(pos);

	LTNode* prev = pos->prev;
	LTNode* newnode = BuyLTNode(x);

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

}

e.在链表中删除节点

void LTErase(LTNode* pos)
{
	assert(pos);

	LTNode* posprev = pos->prev;
	LTNode* posnext = pos->next;

	posprev->next = posnext;
	posnext->prev = posprev;

	free(pos);
	pos = NULL;
}

有了这个函数,也可以让头删和尾删变得相当的简洁。

f.判断链表是否为空

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

	return phead->next == phead;
}

由于头删尾删会改变链表,因此需要一个判空函数来保证程序的安全性

g.头插尾插

//尾插
void LTPushBack(LTNode* phead, LTDataType x)//不需要二级指针(没有改变phead)
{
	assert(phead);

	//LTNode* newnode = BuyLTNode(x);
	//LTNode* tail = phead->prev;
	//tail->next = newnode;
	//newnode->next = phead;
	//newnode->prev = tail;
	//phead->prev = newnode;

	LTInsert(phead, x);

}

//头插
//头插---指的是插在头结点之后,首个含数据的结点之前
void LTPushFront(LTNode* phead, LTDataType x)
{
	assert(phead);

	//LTNode* newnode = BuyLTNode(x);
	//phead->next->prev = newnode;
	//newnode->next = phead->next;

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

	//香饽饽
	LTInsert(phead->next, x);
}

注释掉的是没有依靠Insert和Erase函数来写的头插尾插,是相当麻烦的,通过那两个函数,能让你不到10分钟就能写出双链表。

h.头删尾删

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;

	LTErase(phead->prev);

}

void LTPopFront(LTNode* phead)
{
	assert(phead);
	assert(!LTEmpty(phead));

	//if (phead->next->next == NULL)
	//{
	//	phead->next = phead;
	//	phead->prev = phead;
	//}
	//else
	//{
	//	LTNode* cur = phead->next;
	//	LTNode* af = phead->next->next;

	//	assert(cur);
	//	assert(af);

	//	phead->next = af;
	//	af->prev = phead;
	//	free(cur);
	//	cur = NULL;
	//}

	LTErase(phead->next);

}

i.打印链表

 


void LTPrint(LTNode* phead)
{
	assert(phead);

	printf("guard<==>");
	LTNode* cur = phead->next;


	while (cur != phead)
	{
		printf("%d<==>", cur->data);

		cur = cur->next;
	}
	printf("\n");
}

j.销毁链表

程序执行完毕后需要销毁程序,这样才不会出现内存问题

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;

	LTErase(phead->prev);

}

void LTPopFront(LTNode* phead)
{
	assert(phead);
	assert(!LTEmpty(phead));

	//if (phead->next->next == NULL)
	//{
	//	phead->next = phead;
	//	phead->prev = phead;
	//}
	//else
	//{
	//	LTNode* cur = phead->next;
	//	LTNode* af = phead->next->next;

	//	assert(cur);
	//	assert(af);

	//	phead->next = af;
	//	af->prev = phead;
	//	free(cur);
	//	cur = NULL;
	//}

	LTErase(phead->next);

}

2.3测试程序

实现完函数之后还需要对其进行测试

#include"List.h"

void TestList1()
{
	LTNode* plist = LTInit();
	LTPushBack(plist, 1);
	LTPushBack(plist, 2);
	LTPushBack(plist, 3);
	LTPushBack(plist, 4);
	LTPushBack(plist, 5);
	LTPopFront(plist);

	LTPrint(plist);
	LTPopFront(plist);
	LTPopFront(plist);
	LTPopFront(plist);
	LTPrint(plist);

	LTPopFront(plist);
	LTPrint(plist);

	LTDestroy(plist);
	plist = NULL;

}
void TestList2()
{
	LTNode* plist = LTInit();
	LTPushBack(plist, 1);
	LTPushBack(plist, 2);
	LTPushBack(plist, 3);
	LTPushBack(plist, 4);
	LTPushBack(plist, 5);
	LTPopFront(plist);

	LTPrint(plist);
	LTNode* pos = LTFind(plist, 3);
	if (pos)
	{
		LTInsert(pos, 30);
	}
	LTPrint(plist);
	LTDestroy(plist);
	plist = NULL;
}

int main()
{
	TestList1();
	TestList2();

	return 0;
}

最后在终端上运行一遍得到结果

 结果是这样的小伙伴就是正确掌握了的哟

如果没有跑起来的uu们也不用担心,细心调试一下,慢慢找错也是一种成长。 

3.总结

链表的学习我认为是一个先苦后甜的过程,把单链表的原理搞懂之后,再使用双链表就完全是如鱼得水了。学习也是一样,先吃苦,以后才能尝到生活的甜头。

最后关于链表的问题,我强烈建议大家刷题巩固,踏实稳重,才能把数据结构这个难关拿下。

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

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

相关文章

Tortoisegit 免密配置

TortoiseGit的免密配置通常涉及公钥和私钥的使用&#xff0c;以及通过配置来避免在每次操作时需要输入密码。以下是具体的配置步骤&#xff1a; 1、生成私钥&#xff1a; 首先&#xff0c;使用PuTTYgen生成私钥。你可以在“开始”菜单中找到TortoiseGit文件夹&#xff0c;并在…

AVP-SLAM:自动泊车系统中的语义SLAM_

AVP-SLAM&#xff1a;自动泊车系统中的语义SLAM 附赠最强自动驾驶学习资料&#xff1a;直达链接 ●论文摘要 在自动代客泊车系统中车辆在狭窄且拥挤且没有GPS信号的停车场中进行导航&#xff0c;具备准确的定位能力是至关重要的。传统的基于视觉的方法由于在停车场中由于缺少…

爬虫逆向实战(36)-某建设监管平台(RSA,魔改)

一、数据接口分析 主页地址&#xff1a;某建设监管平台 1、抓包 通过抓包可以发现网站首先是请求了一个/prod-api/mohurd-pub/vcode/genVcode的接口&#xff0c;用于获取滑块验证码的图片 滑块验证之后&#xff0c;请求了/prod-api/mohurd-pub/dataServ/findBaseEntDpPage这…

Git——标签详解

目录 Git标签1、概述1.1、标签是什么1.2、什么时候使用标签1.3、标签的分类 2、轻量标签&#xff08;lightweight tag&#xff09;3、有附注的标签&#xff08;annotated tag&#xff09;4、两种标签的区别5、删除标签 Git标签 1、概述 1.1、标签是什么 在Git中&#xff0c;…

解决Anaconda环境下利用gradio启动web页面生成的链接报错Could not create share link

一、错误信息 启动web页面生成了地址&#xff0c;但是在网页中无法访问&#xff1a; 二、解决方法 在报错的同时也给我们指出了解决方法&#xff1a; Please check your internet connection. This can happen if your antivirus software blocks the download of this fi…

搭建Hadoop集群(完全分布式运行模式)

目录 一、准备模板机(最小化安装)二、配置一台纯净的模板机修改主机名固定IP地址通过yum安装方式安装必要的软件关闭防火墙且禁止自启修改hosts映射文件创建普通用户 并让他能用sudo命令在/opt下创建software和module完成 三、搭建完全分布式运行模式3.1克隆第一台机器hadoop10…

2024-3-18-C++day6作业

1>思维导图 2>试编程 要求: 封装一个动物的基类&#xff0c;类中有私有成员&#xff1a;姓名&#xff0c;颜色&#xff0c;指针成员年纪 再封装一个狗这样类&#xff0c;共有继承于动物类&#xff0c;自己拓展的私有成员有&#xff1a;指针成员&#xff1a;腿的个数&a…

面试算法-52-对称二叉树

题目 给你一个二叉树的根节点 root &#xff0c; 检查它是否轴对称。 示例 1&#xff1a; 输入&#xff1a;root [1,2,2,3,4,4,3] 输出&#xff1a;true 解 class Solution {public boolean isSymmetric(TreeNode root) {return dfs(root, root);}public boolean dfs(Tr…

长安链正式发布三周年,技术更迭支撑产业变革

导语&#xff1a; 2024年1月27日长安链正式发布三周年&#xff0c;开源社区借开年之际与大家一同回顾长安链三年来的技术发展历程&#xff0c;每一个里程碑的建设都得益于与长安链同行的合作伙伴与开发者&#xff0c;希望在2024年可以共同携手继往开来&#xff0c;为数字经济发…

第三门课:结构化机器学习项目-机器学习策略

文章目录 1 机器学习策略一1.1 为什么是ML策略&#xff1f;1.2 正交化1.3 单一数字评估指标1.4 满足和优化指标1.5 训练、开发及测试集划分1.6 开发集和测试集的大小1.7 什么时候改变开发、测试集和指标&#xff1f;1.8 为什么是人的表现&#xff1f;1.9 可避免偏差1.10 理解人…

STM32第九节(中级篇):RCC(第三节)—— 使用HSE配置系统时钟并使用MCO输出监控系统时钟

前言 这节课我们开始学习使用HSE配置系统时钟并使用MCO输出监控系统时钟&#xff0c;上节课我们讲了固件库里的系统时钟配置函数&#xff0c;是机器写的&#xff0c;我们现在自己来写一个。 STM32第九节&#xff08;中级篇&#xff09;&#xff1a;RCC&#xff08;第三节&…

【人工智能】Gitee AI 天数智芯有奖体验开源AI模型,一定能有所收货,快来体验吧

大家好&#xff0c;我是全栈小5&#xff0c;欢迎阅读小5的系列文章。 这是《人工智能》系列文章&#xff0c;每篇文章将以博主理解的角度展开讲解。 目录 前言两大赛道天数智芯1.模型地址2.天数智芯专区3.选择模型4.模型详情页5.部署模型6.成功部署7.执行例子8.移除模型 千模盲…

被大家低估的Excel函数扫地僧choose函数

今天我们要跟大家介绍Excel函数界的又一个世外高手——引用函数CHOOSE。 首先来看下它的基本语法&#xff1a; CHOOSE(索引值,参数1,[参数2],…[参数254]) CHOOSE函数主要用于根据索引值从一组数据中返回相应位置的数值。索引值是介于1到254之间的数字&#xff0c;或者是包含…

2024全网最全的完整的性能测试流程!

完整的性能测试流程 一、准备工作 在什么阶段开展性能测试工作&#xff1f;一般情况下&#xff0c;是在被测系统已完成功能测试、系统趋于稳定的情况下&#xff0c;才会进行性能测试。 1. 组建测试团队 根据被测系统的实际情况&#xff0c;组建一个性能测试团队&#xff0c;团…

[QJS xmake] 非常简单地在Windows下编译QuickJS!

文章目录 前言准备C编译器xmake编译包 工程准备修改版本号第一遍编译第二遍编译效果 前言 quickjs是个很厉害的东西啊&#xff0c;我一直想编译一下的&#xff0c;奈何一直没成功。现在找了点时间成功编译了&#xff0c;写篇文章记录一下。当前版本&#xff1a;2024-1-13 应该…

STM32CubeIDE基础学习-LED闪烁实验

STM32CubeIDE基础学习-LED闪烁实验 文章目录 STM32CubeIDE基础学习-LED闪烁实验前言第1章 硬件介绍第2章 新建工程2.1 基础工程配置部分2.2 工程外设配置部分2.3 生成工程代码部分2.4 输出HEX文件、编译下载 第3章 代码编写3.1 方式1&#xff1a;IO翻转3.2 方式2&#xff1a;调…

“西安大重澳生物科技有限公司”——甄选优质企业品牌入围央媒

西安大重澳生物科技有限公司&#xff0c;一家专注于生物科技研发的企业&#xff0c;自2017年成立以来&#xff0c;致力于开发革命性的肤用肽制剂产品。近日&#xff0c;成功入围央视新媒体直播盛典。在持续的创新与努力下&#xff0c;畅夫泰R畅肤肽品牌已成为备受瞩目的名副其实…

园区配电监测信息系统

园区配电监测信息系统是一款集成了高科技、数据分析和管理的系统&#xff0c;旨在实时监测和控制园区内的电力供应&#xff0c;提高电力使用效率&#xff0c;减少能源浪费&#xff0c;确保电力安全。该系统通过现代通信技术、自动控制技术和计算机技术&#xff0c;实现对园区配…

码云简化版使用教程

码云简化版使用教程 ①创建本地项目 ②在本地项目根目录下创建git相关目录及文件 ③在码云上创建新的仓库 ④在本地项目中配置仓库地址&#xff0c;提交项目内容 下面直接从第二步开始讲解 在本地项目根目录下创建git相关目录及文件 1、打开项目根目录&#xff0c;进入cmd界…

Huggingface 笔记:大模型(Gemma2B,Gemma 7B)部署+基本使用

1 部署 1.1 申请权限 在huggingface的gemma界面&#xff0c;点击“term”以申请gemma访问权限 https://huggingface.co/google/gemma-7b 然后接受条款 1.2 添加hugging对应的token 如果直接用gemma提供的代码&#xff0c;会出现如下问题&#xff1a; from transformers i…