9-静态链表及其有关操作

news2024/9/22 23:22:46

链表可以用malloc/new和结构体加指针的方式来实现,那种实现方式实现的链表又被称为动态链表。但是我们还可以利用数组的方式来实现一个链表,这种实现方式称为静态链表。

静态单链表

我们知道,一个链表节点主要由两大部分组成:数值和一个指向下一个节点的next指针。

而静态链表是用两个数组来分别存储每一个节点左边的数值和右边的指针,其具体的实现方式是用一个数组存储每个链表中的数值,另外一个数组用来存储第一个数组中的每个数值对应的下标(除首节点)。下面是一个按顺序从头节点开始创建的静态链表数组:

 有两个数组后我们还需要一个引子来找到存放数值的数组,这就是数组的头节点。而头节点是不算入链表中的,但是我们的首节点需要被指向,所以我们需要另开一个变量HeadNode来存储它,并且还需要另开一个index变量来表示当前用于存储的数组存储到的位置(当前位置)。

代码形式表达如下(我想创建的是一个最多有100个节点的链表):

const int N = 100;

int HeadNode, val[N], Node[N], index;

接下来第二步就是需要将这个链表先初始化,因为初始时我们还没有开始创建第一个元素,所以此时第一个数组val是空的,第二个存放next指针的数组也是空的(里面没有开始存放下标)。这里我们让初始化时HeadNode变量存放下标-1(链表还没有任何首节点),当前数组未开始开辟所以当前位置index的下标为0。

void init()
{
	HeadNode = -1;
	index = 0;
}

 当我们初始化完链表后,接下来就可以在链表的头节点后加上一个真正的节点了。开辟一个新的节点分为4步,第一步是将数值n存放到val数组里面,而当前val数组存储到index位置。第二步是将创建的节点指向头节点之前指向的位置(当创建第一个节点时,第一个节点next指针部分存放的为-1,因为一个链表最后一个节点(包括没有其它节点,只有头节点时)存放的数组下标为-1)。第三部分就是将头节点指向第一个节点,也就是头节点存放第一个节点在Node里面的下标(记住index是指向当前节点、Node[index]是当前节点的next指针的指向,而index才算当前节点的位置)。第四步就是将当前值加加以便下次存放数据即可。

具体实现如下: 

void Head_add(int n)
{
	val[index] = n;
	Node[index] = HeadNode;
	HeadNode = index;
	index++;
}

接下来可以实现链表的删除操作,例如删除k节点指向的下一个节点,则传递的参数为k-1,本质就是将k节点指向它的下下个节点。如果删除的是首节点,需要额外讨论。对应的关系图如下:

用代码实现如下:

//删除节点
void Delete(int n)
{
	if (n == 0)
	{
		HeadNode = Node[HeadNode];
	}
	else 
	{
		Node[n] = Node[Node[n]];
	}
}

最后就是静态链表的插入问题,在下标为n的节点后面插入一个新节点,原理和在头节点后面新建一个节点类似,都是分为4步:1、创建新节点,将数值x赋予给新节点;2、将新节点的指向修改为下标为n的节点的指向(下标为n的节点的下一个节点);3、将下标为n的节点的指向改为新节点;4、将index加加。注意与删除类似,这里传递的参数为n-1。

//任意插入节点
void Insert(int n, int x)
{
	val[index] = x;
	Node[index] = Node[n];
	Node[n] = index;
	index++;
}

总代码:

#include<iostream>
using namespace std;
const int N = 100;

int HeadNode, val[N], Node[N], index;


//初始化静态链表
void init()
{
	HeadNode = -1;
	index = 0;
}


//在头节点后再插入一个节点
void Head_add(int n)
{
	val[index] = n;
	Node[index] = HeadNode;
	HeadNode = index;
	index++;
}

//删除节点
void Delete(int n)
{
	if (n == 0)
	{
		HeadNode = Node[HeadNode];
	}
	else 
	{
		Node[n] = Node[Node[n]];
	}
}

//任意插入节点
void Insert(int n, int x)
{
	val[index] = x;
	Node[index] = Node[n];
	Node[n] = index;
	index++;
}


int main()
{
	init();
	Head_add(2);
	Head_add(0);
	Head_add(3);
	//Delete(0);
	for (int i = HeadNode; i != -1; i=Node[i])
	{
		//cout << val[i] << ' ';
		cout << Node[i] << ' ';
	}
	return 0;
}

静态双链表

双链表与单链表不同,双链表每个节点都存储三个部分的内容,第一个部分是一个指向上一个节点的指针,第二个部分是数值,第三个部分是一个指向右边的指针;双链表还包括两个端节点,它们不存储数值,一个只存放指向下一个节点的指针,一个只存放指向上一个节点的指针。

要想模拟实现双链表,我们需要三个数组,一个用于存储上一个节点的位置,一个用于存储下一个节点的位置,最后一个用于存储数值。所以我们开辟三个数组存放数值的Val数组、存放左边位置的LeftNode数组和存放右边位置的RightNode数组,同时写一个初始化函数init。初始化先创立左右两个节点,左边端节点右边指向右边端节点,右边端节点左边指向左边端节点,所以三个数组,下标为0我设置为左边端节点,下标为1设置为右边端节点,由于它们都没有数值,所以Val[0]、Val[1]可以不用设置。

const int N = 100;
int index, Val[N], LeftNode[N], RightNode[N];

void init()
{
	LeftNode[1] = 0;
	RightNode[0] = 1;
	index = 2;
}

接下来就可以实现双链表的插入操作了,由于双链表是可以往回走的,所以它会有两种插入方式:左边插入和右边插入,这里我以右边插入为例。

我们要在下标为n的节点后再插入一个节点,它的数值为x,整个操作分为5步。

  • 第一步是将数值赋予给新的节点,也就是先赋值Val[index]。
  • 第二步是将新节点的右边指向下标为n+1的节点。
  • 第三步是将新节点左边指向下标为n的节点。
  • 第四步是将原来下标为n+1的节点左边指向新节点。
  • 第五步是将原来下标为n节点的右边指向新节点。
void RightInsert(int n, int x)
{
	Val[index] = x;
	RightNode[index] = RightNode[n];
	LeftNode[index] = n;
	LeftNode[RightNode[n]] = index;
	RightNode[n] = index;
}

然后就是删除操作,例如删除第n个点(不是下标为n,还有左端点和右端点各算一个,且不能删除),删除操作总的来说就是第n个点左边的点(点1)的右边指向它原来右边的点(点3);然后让其(点2)右边的点(点3)左边指向它原来左边的点(点1)

具体到代码实现如下:

//删掉下标为n的点
void Delete(int n)
{
	LeftNode[RightNode[n]] = LeftNode[n];
	RightNode[LeftNode[n]] = RightNode[n];
}

下面是实现先在左端点右边插入数字1,再在左端点右边插入数字2,最后再在下标为2的数字右边插入数字5,最后删除第4个节点。插入后为215,删除后为21。

int main()
{
	init();
	RightInsert(0, 1);
	RightInsert(0, 2);
	RightInsert(2, 5);
	for (int i = RightNode[0]; i != 1; i = RightNode[i])
		cout << Val[i] << " ";
	cout<<endl;
	Delete(4);
	for (int i = RightNode[0]; i != 1;i = RightNode[i])
		cout << Val[i] << " ";
	return 0;
}

总代码:

#include<iostream>
using namespace std;

const int N = 100;
int index, Val[N], LeftNode[N], RightNode[N];

void init()
{
	LeftNode[1] = 0;
	RightNode[0] = 1;
	index = 2;
}

void RightInsert(int n, int x)
{
	Val[index] = x;
	RightNode[index] = RightNode[n];
	LeftNode[index] = n;
	LeftNode[RightNode[n]] = index;
	RightNode[n] = index;
	index++;
}

//删掉第n个点
void Delete(int n)
{
	LeftNode[RightNode[n]] = LeftNode[n];
	RightNode[LeftNode[n]] = RightNode[n];
}

int main()
{
	init();
	RightInsert(0, 1);
	RightInsert(0, 2);
	RightInsert(2, 5);
	for (int i = RightNode[0]; i != 1; i = RightNode[i])
		cout << Val[i] << " ";
	cout<<endl;
	Delete(4);
	for (int i = RightNode[0]; i != 1;i = RightNode[i])
		cout << Val[i] << " ";
	return 0;
}

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

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

相关文章

黑马Spring学习笔记(一)——IOC/DI核心概念、入门案例

目录 一、Spring系统架构和学习路线 二、Spring核心概念 2.1 IOC、IOC容器、Bean、DI 2.1.1 IOC&#xff08;Inversion of Control&#xff09;控制反转 2.1.2 DI&#xff08;Dependency Injection&#xff09;依赖注入 2.2 核心概念小结 三、入门案例 3.1 IOC入门…

【C++入门】引用、内联函数、auto关键字、基于范围的for循环(C++11)、指针空值nullptr(C++11)

文章目录引用引用概念引用特性引用使用场景常引用内联函数宏的优缺点&#xff1f;C有哪些技术替代宏&#xff1f;auto关键字auto不能推导的场景基于范围的for循环(C11)指针空值nullptr(C11)引用 引用概念 引用不是新定义一个变量&#xff0c;而是给已存在变量取了一个别名&…

一篇搞懂UnitTest

unittest单元测试框架是受到 JUnit 的启发&#xff0c;与其他语言中的主流单元测试框架有着相似的风格。其支持测试自动化、支持将测试样例聚合到测试集中&#xff0c;并将测试与报告框架独立开来。unittest模块是Python标准库中的模块。在此之前需要先了解几个概念&#xff1a…

linux高级命令之线程执行带有参数的任务

线程执行带有参数的任务学习目标能够写出线程执行带有参数的任务1. 线程执行带有参数的任务的介绍前面我们使用线程执行的任务是没有参数的&#xff0c;假如我们使用线程执行的任务带有参数&#xff0c;如何给函数传参呢?Thread类执行任务并给任务传参数有两种方式:args 表示以…

NTC热敏电阻

NTC热敏电阻&#xff1a; 负温度系数热敏电阻。 Negative Temperature Coefficient Thermistor 特性&#xff1a; 由锰 (Mn) 、镍 (Ni) 、钴 (Co) 等成分的氧化物烧制而成的陶瓷。阻值随温度上升而下降。 工作温度范围宽&#xff1a;一般的都可用于-40℃ to 125℃。有的高…

[Pytorch] Linear层输出nan

参考链接&#xff1a; https://discuss.pytorch.org/t/well-formed-input-into-a-simple-linear-layer-output-nan/74720/11 总结原因&#xff1a; numpy需要更新 PS. 查看numpy版本号 打开Anaconda Prompt 进入环境 输入命令conda activate envname 然后输入pip show numpy…

MongoDB 自动增长

MongoDB 自动增长 MongoDB 没有像 SQL 一样有自动增长的功能&#xff0c; MongoDB 的 _id 是系统自动生成的12字节唯一标识。但在某些情况下&#xff0c;可能需要实现 ObjectId 自动增长功能。而MongoDB 并没有提供这个功能&#xff0c;那么可以通过编程的方式来实现_id字段自…

Qt音视频开发14-音视频文件保存基类的设计

一、前言 视频综合应用示例&#xff0c;包括了多种内核&#xff0c;在保存文件这个功能上&#xff0c;需要一个通用的文件保存基类AbstractSaveThread&#xff0c;这个基类定义了是否打印线程消息标志位、直接写入还是排队写入标志位、文件生成后是否调用转换合并标志位、是否…

Linux下的Jenkins安装教程

当前环境 CentOS 7.8Java 11&#xff08;注意当前jenkins支持的Java版本最低为Java11&#xff09;FinalShell 3.9&#xff08;操作环境&#xff09; 安装Jenkins PS&#xff1a;不建议使用Docker安装Jenkins&#xff0c;因为使用Jenkins的时候一般会调用外部程序&#xff0c;…

【猜名次】-C语言-题解

1. 描述&#xff1a; 5位运动员参加了10米台跳水比赛&#xff0c;有人让他们预测比赛结果&#xff1a; A选手说&#xff1a;B第二&#xff0c;我第三&#xff1b; B选手说&#xff1a;我第二&#xff0c;E第四&#xff1b; C选手说&#xff1a;我第一&#xff0c;D第二&#x…

【工作10年+的大厂资深架构师万字长文总结 精华收藏!】怎样设计高可用、高性能系统?关于高可用高性能系统架构和设计理论和经验总结...

本文从研发规范层面、应用服务层面、存储层面、产品层面、运维部署层面、异常应急层面这六大层面去剖析一个高可用的系统需要有哪些关键的设计和考虑.O、前言随着业务在线化互联网化的高速发展&#xff0c;企业对核心业务系统的稳定性、可靠性、有效性、业务连续性等有了更高的…

openpnp - 普通航空插头和PCB的连接要使用线对板连接器

文章目录openpnp - 普通航空插头和PCB的连接要使用线对板连接器概述改进实际效果总结ENDopenpnp - 普通航空插头和PCB的连接要使用线对板连接器 概述 和同学讨论问题, 准备将航空插头连接到PCB上. 航空插头选用GX12-4公头, 拧到开孔的铁板上. 然后航空插头公头再与PCB连接. 铁…

【数据库】HNU数据库系统期末考试复习重点

前言 今天刚结束考试&#xff0c;考的范围基本没有超过这套重点内容&#xff0c;觉得整理的这份资料还算比较有用&#xff0c;遂睡前整理了下分享给大家&#xff0c;希望能帮到要准备数据库期末又时间紧张的学弟学妹~ 文章参考&#xff1a; 1.课程老师发《数据库期末考试复习…

【项目精选】基于网络爬虫技术的网络新闻分析(论文+源码+视频)

基于网络爬虫技术的网络新闻分析主要用于网络数据爬取。本系统结构如下&#xff1a; &#xff08;1&#xff09;网络爬虫模块。 &#xff08;2&#xff09;中文分词模块。 &#xff08;3&#xff09;中3文相似度判定模块。 &#xff08;4&#xff09;数据结构化存储模块。 &…

120个IT冷知识,看完就不愁做选择题了

目录 IT冷知识 01-10 1.冰淇淋馅料 2.蠕虫起源 3.Linux和红帽子 4."间谍软件"诞生 5.游戏主机的灵魂 6.Linux之父 7.NetBSD的口号 8.安卓起源 9.不是第七代的 Win 7 10.域名金字塔 11~20 11.神奇魔盒 12. 第一个Ubuntu 正式版本 13.巾帼英雄 14.密码…

【高可用系统架构设计】SLA服务可用性4个9是什么意思?如何保证服务的高可用性 HA(High Availability)?...

如何保证服务的高可用性 HA&#xff08;High Availability&#xff09;?高可用HA&#xff08;High Availability&#xff09;是分布式系统架构设计中必须考虑的因素之一&#xff0c;它通常是指&#xff0c;通过设计减少系统不能提供服务的时间。方法论上&#xff0c;高可用是通…

springboot整合阿里云oss文件服务器

springboot整合阿里云oss文件服务器一、申请Bucket二、 获取AccessKey ID、AccessKey Secret三、 springboot整合3.1 在application.yml 配置参数3.2 oss需要的pom3.3 配置 oss配置类3.4 oss的controller类3.5 oss的service类以及impl一、申请Bucket 进入该网址对象存储oss述 …

18 二叉树

文章目录1 为什么需要树这种数据结构2 树示意图3 二叉树的概念4 二叉树的遍历5 二叉树的遍历的代码实现6 二叉树的遍历查找的代码实现1 为什么需要树这种数据结构 1) 数组存储方式的分析 优点&#xff1a;通过下标方式访问元素&#xff0c;速度快。对于有序数组&#xff0c;还…

【Linux】进程状态

文章目录1. 阻塞1. 举例2. 为什么要阻塞&#xff1f;3.操作系统层面上如何理解进程等待某种资源就绪呢&#xff1f;资源进程4. 总结2.挂起3.Linux进程状态1. R状态进程只要是R状态&#xff0c;就一定是在CPU运行吗&#xff1f;证明当前进程运行状态生成程序查看进程2. S休眠状态…

New和Malloc的使用及其差异

1&#xff0c;new的使用关于new的定义&#xff1a;new其实就是告诉计算机开辟一段新的空间&#xff0c;但是和一般的声明不同的是&#xff0c;new开辟的空间在堆上&#xff0c;而一般声明的变量存放在栈上。通常来说&#xff0c;当在局部函数中new出一段新的空间&#xff0c;该…