数组模拟常见数据结构

news2025/1/11 8:49:30

我们来学习一下用数组模拟常见的数据结构:单链表,双链表,栈,队列。

用数组模拟这些常见的数据结构,需要我们对这些数据结构有一定的了解哈。

单链表请参考: http://t.csdn.cn/SUv8F

用数组模拟实现比STL要快,在做算法题一般习惯使用数组模拟数据结构。这种数组模拟是非常多的奥,比如:邻接表,哈希表的拉链存储,Trie树,堆等。

  1. 数组模拟单链表

会用到的变量或者数组:

head:表示头结点的下标。
e[i]:表示节点i的值、。
ne[i]:表示节点i的next指针是多少,数组模拟链表的话ne[i]的值就是下标啦。
idx:表示当前使用到了数组(e数组和ne数组的使用是同步的)的哪个位置(下标)。

可以把以上变量和数组定义成全局变量。

#define ARRAY_SIZE 1010
int head; //头结点的下标
int e[ARRAY_SIZE]; //存储数据的数组,表示节点的值
int ne[ARRAY_SIZE]; //存储下一个节点的数组
int idx; //当前可以使用的数组下标

1.1 单链表的初始化

一开始的时候链表中是没有数据的,我们让头结点的下标为-1即可(类比指针实现单链表的空指针),idx初始化为0,代表从数组下标为0往后的位置的可以使用。

//链表的初始化
void init()
{
    //初始时链表中没有节点
    head = -1;
    //可用的下标为0
    idx = 0;
}

1.2 链表的头插

在初始化完一个空链表之后。我们尝试来写头插函数:根用指针实现的单链表类似,数组模拟的单链表实现头插需要一下四步:

1:将要插入的值存储到e数组
2:连接原来的头结点
3:更新新的头结点
4:更新可用的下标值

下面是链表中没有数据的情况:

下面是链表中有数据的情况:

通过上面两种情况的分析我们发现无论链表中是否有数据,都可以用这四步来做。那么我们就可以写出头插的代码啦!还有就是通过对指针实现的单链表的理解:尾插的效率是很慢的,所以数组模拟时不再写尾插函数。

//单链表的头插
//假设数组中存储的都是整型数据哈,如果要存其他的数据类型,可以typedef一下
void ListPushFront(int x)
{
    //存值
    e[idx] = x;
    //连接
    ne[idx] = head;
    //更新
    head = idx;
    idx++;
}

1.3 在下标为k的节点的后面插入值为x的节点

同样通过对指针实现的单链表理解:在一个节点的前面插入节点的时间复杂度很高,我们选择在一个节点之后插入新的节点。数组模拟链表时,就是在下标为k的后面插入新的节点啦!

同样也需要四步操作哈:

1:将要插入的值存储到e数组
2:将新的节点连接到k节点的下一个节点
3:将k这个节点连接到新的节点
4:更新可用的下标值(idx)

同样地,对于尾插这样的四步操作也是没有啥问题的,行,我们就可以写出在在下标为k的节点的后面插入值为x的节点的代码啦!

//指定下标k后面插入x
//调用这个函数你得确保k是合法的才行撒,即k下标是链表中的一个节点
void ListInsertAfter(int k, int x)
{
    //将要插入的值存储到e数组
    e[idx] = x;
    //将新的节点连接到k节点的下一个节点
    ne[idx] = ne[k];
    //将k这个节点连接到新的节点
    ne[k] = idx;
    //更新可用的下标值(idx)
    idx++;
}

1.4 将下标为k的节点的后面那个节点删除

同样地,我们不删除前面的节点,时间复杂度太高了哦!在理解了指针版的删除指定位置之后的节点,数组模拟的链表删除指定下标的节点的后面那个节点也是信手拈来好吧!

步骤只有一步哦:

直接让下标为k的节点指向:下标为k的节点的下一个节点的下一个节点就好啦!

是不是很简单 😊

我们发现这个删除只是在逻辑上删除了哈,内存上并没有像指针实现的链表那样删除。也就是说数组模拟的链表被删除的节点理论上还是可以使用的,但实际上并不会再去使用那块空间了,而是使用下标为idx的空间。

//删除指定下标k的后面那个节点
//放到具体的题目中去,k会是合法的哦,直接看代码是有问题的
void ListEraseAfter(int k)
{
    //连接
    ne[k] = ne[ne[k]];
}

1.5 链表的打印

和指针实现的单链表类似,只不过结束打印的条件是:i = -1,我们用的是-1代表空节点嘛!

为了好看,打印的函数还是和指针模拟单链表时的打印函数差不多!

//打印链表
void ListPrint()
{
    //用i遍历链表
    for (int i = head; i != -1; i = ne[i])
    {
        printf("%d->", e[i]);
    }
    printf("NULL");
}
  1. 数组模拟双链表

会用到的变量或者数组:

e[i]:表示节点i的值、。
l[i]:表示节点i的prev指针是多少,数组模拟链表的话l[i]的值就是下标啦。
r[i]:表示节点i的next指针是多少,数组模拟链表的话r[i]的值就是下标啦
idx:表示当前使用到了数组(e数组和l数组和r数组的使用是同步的)的哪个位置(下标)。

同样你可以把以上变量和数组定义为全局变量:

//数组的大小
#define ARRAY_SIZE 1010
//存节点的值
int e[ARRAY_SIZE];
//存节点的上一个节点
int l[ARRAY_SIZE];
//存节点的下一个节点
int r[ARRAY_SIZE];
//可用的数组下标
int idx;

2.1 链表的初始化

初始化双链表时,我们让双链表有一个小小的结构,有了这个结构能方便我们的插入和删除,可以类比带头双向循环链表中的初始化函数让哨兵位的头结点的next和prev均指向自己,这样做就不用考虑什么头插,尾插,头删,尾删的情况了,即通过哨兵位的头结点能够让插入,删除函数具有普适性。数组模拟双向链表中的初始的小结构,也就是这个目的。

这两个节点并不存储数据的喔,只是方便后续的操作。我们令下标为0的位置代表左侧的那个节点,可以理解为head,下标为1的位置代表右侧的那个节点,可以理解为tail。那么初始化时,idx就得从2这个下标开始咯,并且r[0] = 1,代表head指向tail;l[1] = 0,代表tail指向head。

//链表的初始化,初始化链表的结构
void ListInit()
{
    //head向右指向tail
    r[0] = 1;
    //tail向左指向head
    l[1] = 0;
    //因为0代表head,1代表tail所以idx从2开始
    idx = 2;
}

2.2 在下标为k的节点的后面插入一个新的节点

嘿嘿,双链表的在下标为k的位置左右插入一个新节点可以只写一种插入方式即可哦!

我们先来看看在下标为k的节点的后面插入一个新的节点:

只需要以上4步哦,代码的图解就没啥必要了,原理和单链表的插入一个逻辑。

//在下标为k的节点的后面插入一个值为x的节点
void ListInsertAfter(int k, int x)
{
    //存储x
    e[idx] = x;
    //对应步骤1
    r[idx] = r[k];
    //对应步骤2
    l[idx] = k;
    //对应步骤3
    l[r[k]] = idx;
    //对应步骤4
    r[k] = idx;
    //更新可用的下标值
    idx++;
}

emm,那么我们如果想要在下标为k的节点的前面的插入一个节点呢?当然我们可以用上面向后插的逻辑,重新写一个向前插入的函数。但是没有必要哦!

2.3 删除下标为k的节点

这个操作只需要两步哈:

1:下标为k的节点的前一个节点向右指向下标为k的节点的下一个节点。
2:下标为k的节点的下一个节点向左指向下标为k的节点的上一个节点。

方法:

1:l[k]:找到下标为k的节点的上一个节点;r[k]:找到下标为k的节点的下一个节点;r[l[k]] = r[k]:将找到的上一个节点向右连接到找到的下一个节点。

2:r[k]:找到下标为k的节点的下一个节点;l[k]:找到下标为k的节点的上一个节点;l[r[k]] = l[k]:将找到的下一个节点向左连接到找到的上一个节点。

//删除下标为k的节点
void ListErase(int k)
{
    //步骤1
    r[l[k]] = r[k];
    //步骤2
    l[r[k]] = l[k];
}

3. 数组模拟栈

数组模拟栈和队列就非常滴简单了啊!应用请参考单调栈:

http://t.csdn.cn/uBst3

我们会用到的变量和数组:

1:stack[N]:用来模拟栈的数组。
2:top:用来表示栈顶的下标。

你同样可以把他们定义成全局变量:

//模拟栈的数组的大小
#define N 1010
//模拟栈的数组
int stack[N];
//用来表示栈顶元素的下标
int top;

3.1 栈的初始化

我们习惯模拟栈的数组是从下标为1的位置开始存储数据的,因为这样很好判断栈是否为空。既然你将top定义成了全局变量,自然就不用初始化了哦!

3.2 添加元素

添加元素是非常的简单啊:先让top++,然后赋值就行了。

//添加元素
void StackPush(int x)
{
    stack[++top] = x;
}

3.3 删除元素

top--就行。

//弹出栈顶元素
void StackPop()
{
    //top为0栈为空,不用删
    if(top)
        top--;
}

3.4 判断栈是否为空

根据top的值判断即可。

//判断栈是否为空,为空返回true
bool StackEmpty()
{
    return top > 0;
}

3.5 查看栈顶元素

stack[top] 就行了哈。

4. 数组模拟队列

我们会用到的变量和数组:

1:q[N]:用来模拟队列的数组。
2:hh:用来表示队列队头。
3:tt:用来表示队列的队尾。

我们习惯是hh初始化为0,tt初始化为-1,从下标为0的位置开始存储数据。

下面的是伪代码哈!能传达意思就行。具体的应用请参考单调队列!

//模拟栈的数组
int stack[N];
//用来表示栈顶元素的下标
int top;

//模拟队列的数组大小
#define N 1010
//模拟队列的数组
int q[N];
//表示队头
int hh;
//表示队尾
int tt = -1;

//插入元素-队尾入元素
q[++tt] = x;

//弹出元素,队头出数据
hh++;

//判断队列是否为空
if (hh <= tt)
    not empty;
else
    empty;

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

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

相关文章

PCB板漏孔、漏槽怎么办?看工程师避坑“SOP”

本文为大家介绍PCB画板时常见的钻孔问题&#xff0c;避免后续踩同样的坑。钻孔分为三类&#xff0c;通孔、盲孔、埋孔。不管是哪种孔&#xff0c;孔缺失的问题带来的后果是直接导致整批产品不能使用。因此钻孔设计的正确性尤为重要。 案例讲解 问题1&#xff1a;Altium设计的文…

Linux 进程:进程状态

目录一、进程状态1.简单分类2.详细分类&#xff08;1&#xff09;运行态&#xff08;2&#xff09;休眠态[1]可中断休眠态[2]不可中断休眠态&#xff08;3&#xff09;停止状态&#xff08;4&#xff09;死亡状态&#xff08;5&#xff09;僵死状态二、特殊进程1.僵尸进程2.孤儿…

Java-枚举类的使用(详解)

枚举类的使用前言一、何为枚举类&#xff1f;二、自定义枚举类&#xff08;JDK1.5之前&#xff09;1、实现1.1 属性1.2 构造器2、代码演示三、用关键字enum定义枚举类&#xff08;JDK 1.5&#xff09;1、实现1.1 属性1.2 构造器2、代码演示四、Enum类的方法五、实现接口的枚举类…

GeoServer 存在 sql 注入漏洞

漏洞描述 GeoServer 是一个允许用户共享和编辑地理空间数据的开源软件服务器&#xff0c;支持 OGC Filter expression 和 OGC Common Query Language 语言&#xff0c;使用 PostGIS Datastore 作为数据库。PostGIS是PostgreSQL数据库的扩展程序&#xff0c;增加了数据库对地理…

HTMLCollection 和 NodeList 区别

Node 和 Element DOM 是一棵树&#xff0c;所有节点都是 NodeNode 是 Element 的基类Element 是其他 HTML 元素的基类&#xff0c;如 HTMLDivElement HTMLCollection 和 NodeList HTMLCollection 是 Element 的集合NodeList 是 Node 的集合 <body><p id"p1&qu…

什么是IP地址?

IP协议中还有一个非常重要的内容&#xff0c;那就是给因特网上的每台计算机和其它设备都规定了一种地址&#xff0c;叫做“IP 地址”。由于有这种地址&#xff0c;才保证了用户在连网的计算机上操作时&#xff0c;能够高效而且方便地从千千万万台计算机中选出自己所需的对象来。…

堆叠和集群(详细的理论和实验)

随着企业的发展&#xff0c;企业网络的规模越来越大&#xff0c;这对企业网络提出了更高的要求&#xff1a;更高的可靠性、更低的故障恢复时间、设备更加易于管理等。传统的园区网高可靠性技术出现故障时切换时间很难做到毫秒级别、实现可靠性的方案通常为一主一备&#xff0c;…

前端一面react面试题指南

React 事件机制 <div onClick{this.handleClick.bind(this)}>点我</div>React并不是将click事件绑定到了div的真实DOM上&#xff0c;而是在document处监听了所有的事件&#xff0c;当事件发生并且冒泡到document处的时候&#xff0c;React将事件内容封装并交由真正…

2023年3月实时获取地图边界数据方法,省市区县街道多级联动【附实时geoJson数据下载】

首先&#xff0c;来看下效果图 在线体验地址&#xff1a;https://geojson.hxkj.vip&#xff0c;并提供实时geoJson数据文件下载 可下载的数据包含省级geojson行政边界数据、市级geojson行政边界数据、区/县级geojson行政边界数据、省市区县街道行政编码四级联动数据&#xff0…

【C语言进阶】指针进阶

今日所做之事勿候明天&#xff0c;自我所做之事勿候他人。 --歌德 目录 指针进阶(更深层次的理解): 一.字符指针 二.指针数组 ​三.数组指针 1.数组指针的定义: 2.&数组名和数组名: 3.数组指针的使用: 四.数组参数&#xff0c;指针参数 1.一维数组传参:…

好代码的五个特质

CUPID出自Daniel的一篇名为《CUPID—for joyful coding》的博文&#xff0c;即《CUPID-为了快乐编程》。CUPID是Composable/Unix philosophy/Predictable/Idiomatic/Domain based几个单词的缩写&#xff0c;有经验的同学一看就知道这是好代码的一些属性。知道Cupid这个单词的同…

软件测试3

一 路径 路径&#xff1a;写想要找到的资源位置。 1.相对路径&#xff1a;相对于当前html文件去找其他资源的方式 &#xff08;1&#xff09;同级查找&#xff1a;当前html和目标在同一级别目录中&#xff0c;语法&#xff1a;直接写资源的名字即可 &#xff08;2&#xff09…

林心如常驻《向往的生活》,周杰却陷地域黑,做人的差别太大了吧

十年前如果有人提起周杰&#xff0c;就算是不能如雷贯耳&#xff0c;最起码也是妇孺皆知&#xff0c;毕竟那时候他太有名气了。因为拍摄《还珠格格》&#xff0c;让他和林心如等人一起爆红&#xff0c;不过此后的林心如&#xff0c;却很少再有优秀作品问世。 而周杰却不一样&am…

CCNP350-401学习笔记(易错题合集)

CCNP350-401学习笔记&#xff08;1-50题&#xff09;_殊彦_sy的博客-CSDN博客CCNP350-401学习笔记&#xff08;2023.2.17&#xff09;https://blog.csdn.net/shuyan1115/article/details/129088574?spm1001.2014.3001.5502CCNP350-401学习笔记&#xff08;51-100题&#xff09…

Win10系统开始菜单无法点击解决方法分享

Win10系统开始菜单无法点击解决方法分享。有用户电脑一开机之后&#xff0c;就出现了开始菜单无法正常点击的情况。我们很多设置项都是通过开始菜单来进行开启的。那么这个功能无法点击了怎么办呢&#xff1f;接下来我们一起来看看以下的解决方法分享吧。 方法一&#xff1a; 1…

面试官:给你一段有问题的SQL,如何优化?

大家好&#xff0c;我是飘渺&#xff01;我在面试的时候很喜欢问候选人这样一个问题&#xff1a;“你在项目中遇到过慢查询问题吗&#xff1f;你是怎么做SQL优化的&#xff1f;”很多时候&#xff0c;候选人会直接跟我说他们在编写SQL时会遵循的一些常用技巧&#xff0c;比如&a…

【力扣(LeetCode)】【MySQL】【185.部门工资前三高的所有员工】

学习时间&#xff1a; 2023年3月1日 题目描述&#xff1a; 题解分享&#xff1a; # 作 者 : 繁 华 倾 夏# 力扣(LeetCode):185. 部门工资前三高的所有员工# MySQL 数据库selectd.Name as Department, e1.Name as Employee, e1.Salary fromEmployee e1joinDepartment d on …

数据结构初阶 -- 顺序表

数据结构初阶 链表的讲解 目录 一. 线性表 1.1 定义 1.2 特点 二. 顺序表 2.1 定义 2.2 代码 2.3 功能需求 2.4 静态顺序表的特点以及缺点 2.5 动态的顺序表 2.6 动态顺序表接口的实现 三. 代码 头文件 主文件 一. 线性表 1.1 定义 线性表&#xff08;linear li…

【算法】Tire字符串

作者&#xff1a;指针不指南吗 专栏&#xff1a;算法篇 &#x1f43e;或许会很慢&#xff0c;但是不可以停下&#x1f43e; 文章目录1.Trie的基本思想1.1什么是Trie1.2字符串条件1.3如何存储字符串1.4如何查找字符串2.Trie的代码实现2.1怎么用数组建树2.2完整代码1.Trie的基本思…

柔性电路板的优点、分类和发展方向

柔性电路板是pcb电路板的一种&#xff0c;又称为软板、柔性印刷电路板&#xff0c;主要是由柔性基材制作而成的一种具有高可靠性、高可挠性的印刷电路板&#xff0c;具有厚度薄、可弯曲、配线密度高、重量轻、灵活度高等特点&#xff0c;主要用在手机、电脑、数码相机、家用电器…