【数据结构和算法】使用数组的结构实现链表(单向或双向)

这篇具有很好参考价值的文章主要介绍了【数据结构和算法】使用数组的结构实现链表(单向或双向)。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

上文我们通过结构体的结构实现了队列、以及循环队列的实现,我们或许在其他老师的教学中,只学到了用结构体的形式来实现链表、队列、栈等数据结构,本文我想告诉你的是,我们可以使用数组的结构实现链表、单调栈、单调队列

目录

前言

一、用数组结构的好处

1.数组的优缺点

2.链表的优缺点

3.总结

二、用数组实现链表

1.认识构造、初始化

2.将x插入到头结点

3.将x插入到第k次插入数值之后的位置

4.删除第k次插入的结点

三、完整代码演示

四、数组实现双向链表

1.初始化

2.在第k次插入的点的右边插入x

3.删除第k个点

五、完整代码


前言

你之前实现链表的形式,是不是这一种结构来实现

typedef struct ListNode {
	int data;
	struct ListNode* next;
}List;

但是我如果告诉你只需要这样两个数组就能模拟实现链表,你相信吗!!!

head	表示头节点
e[N]	表示存储结点数值的数组
ne[N]	表示结点的下一个结点的位置
idx   表示当前存储元素的位置   当前存储到哪里了就是

 接下来我们来实现单链表,以及双向链表

一、用数组结构的好处

我们在日常学习的过程中,老师教授给我们的都是以结构体的形式实现的链表,但是呢,比如我们要创建100000个结点,这样的话, 用结构体的话,时间太长,空间太大,反观数组,就显得很有优势了。

1.在搞算法的时候,使用数组的形式去模拟链表,会使得运算速度变快,更加适合写算法,打比赛的小朋友。

2.在笔试的适合,会更快的创建实现链表的基础功能,进行插入删除元素,并根据下标直接找到所需元素等

我们在来了解一下数组和链表的优缺点吧

1.数组的优缺点

认识数组:

数组是一种线性结构,存储的空间是内存连续的(物理连续),每当创建一个数组的时候,就必须先申请好一段指定大小的空间。(一次申请即可指定大小的空间)

优点:

由于内存连续这一特征,数组的访问速度很快,直到索引下标之后,可以实现O(1)的时间复杂度的访问。

缺点:

1.在任意位置删除和插入操作的时候,就会涉及到部分元素的移动,这样的话我们对于数组的任意位置的删除和插入的操作的时间复杂度为O(n)。

比如:

1>在i点后面插入数据,那么就需要i+1位置以及之后的元素,整体后移一位(for循环操作),然后再将插入的数据放在i+1的位置上

2>在i点之后删除元素,那么就需要,将i+1以及之后的元素,整体前移一位,总元素个数减一

以上是数组的优缺点,可以快速访问,达到O(1),但是在任意删除和插入元素的时候,会耗时间,达到O(n)。

2.链表的优缺点

认识链表

1.链表也是一种线性结构,但是他存储空间是不连续的(物理不连续,逻辑连续),链表的长度是不确定且支持动态扩展的。每次插入元素的时候,都需要进行申请新节点,然后赋值,插入链表中。

优点:
在插入数据或者删除数据的时候,只需要改变指针的指向即可,不需要类似于数组那样部分整体移动,整个过程不涉及元素的迁移,因此链表的插入和删除操作,时间复杂度为O(1)

缺点:

在查找任意位置的结点的数值域的时候,需要遍历,时间复杂度为O(n)

但是我们在任意位置插入或者删除元素的时候,需要查找这个指定的元素的结点位置,所以综合起来,链表的插入和删除仍为O(n)。

3.总结

无论数组还是链表,查找的时间复杂度都是O(n),查找都要挨个遍历,直到找到满足的条件的数据为止,所以对于链表,如果没有给定,指针的地址,只是要插入删除第N位元素的时候,加上查找,综合起来时间复杂度为O(n)。

但是我们如果以数组的形式来实现链表,那么插入删除指定元素位置的时候,是不是就更加简便了呢,在第N位插入删除元素的时候,直接以O(1)的时间复杂度找到该位置结点,然后再由于链表的删除插入都是O(1)的,所以整个删除或插入操作,综合时间复杂度为O(1),比普通链表快很多。

二、用数组实现链表

1.认识构造、初始化

我们先由图示了解初始化的时候的准备工作

用数组实现链表,数据结构和算法,链表,数据结构,算法

我们使用c++会更加方便理解,因为c++支持用变量来定义数组

初始化代码:

//使用c++更简单,先用c++的形式实现
const int N = 100010;
int head, e[N], ne[N], idx; //全局变量
void init() {
	head = -1;
	idx = 0;//进行初始化的操作,idx为当前链表中(数组)最后一个元素(末尾),下标位置
}

2.将x插入到头结点

就是所谓的链表中的头部插入

图示:
用数组实现链表,数据结构和算法,链表,数据结构,算法

实际上和普通链表的头插一样,只是流程next指针换成了ne[N]数组的形式,记录的是下一结点的数值。

代码如下:

void add_to_head(int x) {
	e[idx] = x;//将x数值存入到e[]数组中
	ne[idx] = ne[head];//将idx新插入的结点的下一个位置存储到ne[idx]中  ,全局变量 ne以及n数组初始化为0
	head = idx;
	idx++;
}

3.将x插入到第k次插入数值之后的位置

图示:
用数组实现链表,数据结构和算法,链表,数据结构,算法

我们要说明一个问题,ne[N]这个数组存放的数值,是不需要管的,因为不管是add还是remove,插入还是删除结点,都不会重复,实际上,e[ne[head]]是得到head结点下一个结点的数值,ne[]数组只作为,下标使用。 

我们是在第k次插入的之后的位置插入x,但是与此同时 ,此时的idx成为新的第k次插入数据下标,也就是说,再次对第k次插入数值之后的位置插入的x,实际上是在上一次的新插入的结点之后进行插入

图示:

用数组实现链表,数据结构和算法,链表,数据结构,算法

代码如下:

//在第k个插入的数字之后插入数据
void add(int k, int x) {
	e[idx] = x;
	ne[idx] = ne[k];
	ne[k] = idx;
	idx++;
}

4.删除第k次插入的结点

//将下标为k的点后面的点删掉
void remove(int k, int ne[]) {
	ne[k] = ne[ne[k]];//表示k的下一个位置(ne)为下一个位置的下一个位置,这样跳过了原来的ne[k]结点
}//使用的时候应该是  删除的是k之后的点

直接跳过即可,和链表类似

三、完整代码演示

#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<stdlib.h>


const int N = 100010;
int n, e[N], ne[N], idx, head;


//初始化
void init() {
	head = -1;
	idx = 0;
}

//头插
void add_to_head(int x) {
	e[idx] = x;
	ne[idx] = head;
	head = idx;
	idx++;
}

//在第k个插入的数字之后插入数据
void add(int k, int x) {
	e[idx] = x;
	ne[idx] = ne[k];
	ne[k] = idx;
	idx++;
}
//删除第k的插入的数据
void remove(int k) {
	ne[k] = ne[ne[k]];
}

int main()
{
	init();
	add_to_head(1);
	add_to_head(2);
	add_to_head(3);
	add_to_head(4);
	add_to_head(5);
	add(2-1, 10);
	add(2-1, 2);
	add(2-1, 3);
	add(2-1, 4);
	add(2-1, 5);
	add_to_head(50);
	for (int i = head; i != -1; i=ne[i]) {
		printf("%d ", e[i]);
	}
	printf("\n");
	for (int i = head; i != -1; i = ne[i]) {
		printf("%d ", ne[i]);
	}
	return 0;
}

四、数组实现双向链表

1.初始化

//初始化
// e[]表示节点的值,l[]表示节点的左指针,r[]表示节点的右指针,idx表示当前用到了哪个节点
int m;
const int N = 100010;
int e[N], l[N], r[N], idx;
void init() {
	//0表示为左端点,1表示为右端点
	r[0] = 1;
	l[1] = 0;
	idx = 2;//从2开始
}

我们设定,用e[N]数组来记录数据,在用 l[N] 、 r[N]数组表示结点的左右指针,一开始,0表示为左端点,1表示右端点,然后r、l两个数组记录,数值下标从2开始

e[]表示节点的值,l[]表示节点的左指针,r[]表示节点的右指针,idx表示当前用到了哪个节点文章来源地址https://www.toymoban.com/news/detail-807123.html

2.在第k次插入的点的右边插入x

//在第k次插入的点的右边插入x;

void add(int k, int x) {
	e[idx] = x;//数值x给当前idx位置的e数组存储
	r[idx] = r[k];//将新节点的左右两端分别连接k的后一个结点r[k]和k本身
	l[idx] = k;
	l[r[k]] = idx;//然后将k的右端点的左端点连接idx
	r[k] = idx++;//最后将k的右端点连接idx
}

3.删除第k个点

//删除第k个点
void remove(int k) {
	//就是将k的左端点和右端点相互连接
	l[r[k]] = l[k];
	r[l[k]] = r[k];
}

五、完整代码

#include<stdio.h>
#include<stdlib.h>
#include<malloc.h>
#include<assert.h>
//使用数组的形式实现双向链表


//e[N]	表示存储结点的数值
//l[N]	表示当前结点的左结点位置
//r[N]	表示当前结点的右节点位置
//idx	表示当前结点存储的位置

//初始化
int m;
const int N = 100010;
int e[N], l[N], r[N], idx;
void init() {
	//0表示为左端点,1表示为右端点
	r[0] = 1;
	l[1] = 0;
	idx = 2;//从2开始
}

//在第k次插入的点的右边插入x;

void add(int k, int x) {
	e[idx] = x;//数值x给当前idx位置的e数组存储
	r[idx] = r[k];//将新节点的左右两端分别连接k的后一个结点r[k]和k本身
	l[idx] = k;
	l[r[k]] = idx;//然后将k的右端点的左端点连接idx
	r[k] = idx++;//最后将k的右端点连接idx
}

//删除第k个点
void remove(int k) {
	//就是将k的左端点和右端点相互连接
	l[r[k]] = l[k];
	r[l[k]] = r[k];
}

int main()
{
	init();//初始化
	//添加元素
	for (int i = 0; i < 10; i++)
	{
		add(i, i);//在第i次插入的元素的后面,插入节点x
	}
	//实现遍历

	//因为e存储的是实际元素,但是由于要实现双向链表,即创建左右数组,表示当前idx的左右下标
	//如果我们需要进行正向,即向右,只需要以r[i]为下标,不断输出e的元素即可
	//反之以l[i]
	for (int i = 0; i < 10; i++)
	{
		//num表示当前此时下标位置
		cout << e[r[i]] << " ";//每一次循环不断更新当前节点的下一个节点的下标位置
	}

	cout << endl;
	for (int i = 0; i <10 ; i++)
	{
		//idx表示当前此时下标位置
		cout << e[--idx] << " ";//每一次循环不断更新当前节点的下一个节点的下标位置
	}
	//以上是正序和逆序的遍历
	cout << endl;

	return 0;
}

到了这里,关于【数据结构和算法】使用数组的结构实现链表(单向或双向)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包赞助服务器费用

相关文章

  • 【数据结构与算法——TypeScript】算法的复杂度分析、 数组和链表的对比

    【数据结构与算法——TypeScript】算法的复杂度分析、 数组和链表的对比

    什么是算法复杂度(现实案例)? ❤️‍🔥 前面已经解释了什么是算法? 其实就是解决问题的一系列步骤操作、逻辑。 ✅ 对于同一个问题,我们往往有很多种解决思路和方法,也就是可以采用不同的算法。 举个例子(现实例子):在一个庞大的图书馆中,我们需要找一本书

    2024年02月14日
    浏览(6)
  • Java 数据结构篇-用链表、数组实现栈

    Java 数据结构篇-用链表、数组实现栈

    🔥博客主页: 【 小扳_-CSDN博客】 ❤感谢大家点赞👍收藏⭐评论✍    文章目录         1.0 栈的说明         2.0 用链表来实现栈         2.1 实现栈 - 入栈方法(push)         2.2 实现栈 - 出栈(pop)         2.3 实现栈 - 查看栈顶元素(peek)         2.4 实

    2024年02月05日
    浏览(16)
  • 【数据结构与算法】之双向链表及其实现!

    【数据结构与算法】之双向链表及其实现!

    ​                                                                                 个人主页:秋风起,再归来~                                                                                             数据结构与

    2024年04月23日
    浏览(11)
  • 【数据结构与算法】 - 双向链表 - 详细实现思路及代码

    【数据结构与算法】 - 双向链表 - 详细实现思路及代码

    前几篇文章介绍了怎样去实现单链表、单循环链表, 这篇文章主要介绍 双向链表 以及实现双向链表的步骤,最后提供我自己根据理解实现双向链表的C语言代码 。跟着后面实现思路看下去,应该可以看懂代码,看懂代码后,就对双向链表有了比较抽象的理解了,最后自己再

    2024年02月01日
    浏览(5)
  • 【数据结构 | 入门】线性表与链表 (问题引入&实现&算法优化)

    【数据结构 | 入门】线性表与链表 (问题引入&实现&算法优化)

    🤵‍♂️ 个人主页: @计算机魔术师 👨‍💻 作者简介:CSDN内容合伙人,全栈领域优质创作者。 本文是浙大数据结构学习笔记专栏 这里我们引入一个问题,最常见的多项式,我们如何使用编程将多项式表示出来呢? 我们可以使用数组来表示,但是会随着一个问题,如下图底

    2024年01月21日
    浏览(49)
  • 数据结构——线性数据结构(数组,链表,栈,队列)

    数据结构——线性数据结构(数组,链表,栈,队列)

    数组(Array) 是一种很常见的数据结构。它由相同类型的元素(element)组成,并且是使用一块连续的内存来存储。 我们直接可以利用元素的索引(index)可以计算出该元素对应的存储地址。 数组的特点是: 提供随机访问 并且容量有限。 2.1. 链表简介 链表(LinkedList) 虽然是

    2024年02月11日
    浏览(10)
  • 【数据结构和算法】实现带头双向循环链表(最复杂的链表)

    【数据结构和算法】实现带头双向循环链表(最复杂的链表)

    前文,我们实现了认识了链表这一结构,并实现了无头单向非循环链表,接下来我们实现另一种常用的链表结构,带头双向循环链表。如有仍不了解单向链表的,请看这一篇文章(7条消息) 【数据结构和算法】认识线性表中的链表,并实现单向链表_小王学代码的博客-CSDN博客

    2024年01月17日
    浏览(10)
  • C++数据结构与算法详解:链表、栈、队列、树、二叉树和图结构的实现与应用

    链表是一种常见的数据结构由一系列节点按顺序连接而成,一般每个节点包含一个数据元素和一个指向下一个节点的引用。 链表有多种类型: 单向链表:每个节点只有一个指向下一个节点的引用 双向链表:每个节点有一个指向前一个节点和一个指向后一个节点的引用 循环链

    2024年02月04日
    浏览(7)
  • 数据结构----链表介绍、模拟实现链表、链表的使用

    数据结构----链表介绍、模拟实现链表、链表的使用

    ArrayList底层使用连续的空间,任意位置插入或删除元素时,需要将该位置后序元素整体往前或者往后搬移,故时间复杂度为O(N) 增容需要申请新空间,拷贝数据,释放旧空间。会有不小的消耗。 增容一般是呈2倍的增长,势必会有一定的空间浪费。例如当前容量为100,满了以后

    2024年02月21日
    浏览(8)
  • 【数据结构与算法】1、学习动态数组数据结构(基本模拟实现 Java 的 ArrayList 实现增删改查)

    【数据结构与算法】1、学习动态数组数据结构(基本模拟实现 Java 的 ArrayList 实现增删改查)

    🍃 数据结构是 计算机 存储 、 组织 数据的方式 🎉 线性 结构 线性表(数组、链表、栈、队列、哈希表) 🎉 树形 结构 二叉树 AVL 树 红黑树 B 树 堆 Trie 哈夫曼树 并查集 🎉 图形 结构 邻接矩阵 邻接表 🎁 线性表是具有 n 个 相同类型元素 的有限 序列 (n = 0) a1 是首节点

    2024年02月10日
    浏览(8)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包