算法模版:模拟数据结构之链表

简介: 算法模版:模拟数据结构之链表

前言


唤我沈七就好。

本专题的绪论部分里已经简单的解释了什么是数据结构,以及有哪些常见的数据结构。

准备工作完毕之后。

接下来我们就开始进入本板块的正文部分,

模拟数据结构之链表

还是那句话。

蒟蒻因为实在是太弱了,肯定免不了错误百出。

欢迎批评指正,期待共同成长!

什么是链表?


回答这个问题,就不得不提一下链表的孪生兄弟—数组了,其实这两者都属于同一种数据结构—线性表。

但因为存储结构的不同而被划分成了两类。

数组是顺序存储结构,而链表则是链式存储。

关于数组想必各位都已经很熟悉了,所以这次主要讲解模拟链表部分。

在此之前先来谈谈数组在使用时存在的问题。

想必最令人头痛的应该就是数组在插入和删除时需要移动大量元素,这显然是非常耗费时间的。

而链表的出现则很好的解决了这个问题。

链式存储结构的特点是用一组任意的存储单元存储线性表的数据元素,这组存储单元可以是连续的,也可以是不连续的。

这就意味着,这些数据元素可以存在内存未被占用的任意位置(如图3-6-1所示)。

5.png



实现思路


为什么当插入和删除时,就要移动大量元素,仔细分析后,发现原因就在于相邻两元素的存储位置也具有邻居关系。

它们编号是1,2,3,…,n,它们在内存中的位置也是挨着的,中间没有空隙,当然就无法快速介入,而删除后,当中就会留出空隙,自然需要弥补。问题就出在这里。


那如何解决呢?


我们反正也是要让相邻元素间留有足够余地,那干脆所有的元素都不要考虑相邻位置了。

哪有空位就到哪里,只是让每个元素知道它下一个元素的位置在哪里。这样,我们可以在第一个元素时,就知道第二个元素的位置(内存地址),而找到它;

在第二个元素时,再找到第三个元素的位置(内存地址)。这样所有的元素我们就都可以通过遍历而找到。


这思路是不是很妙,难点就是如何实现让每一个元素知道它下一个元素的位置在哪里。

而我们恰巧有一个现成的东西----指针。

但考虑到用指针操作略显复杂,

所以我们这里用下标来作为地址,用一个新数组来代替指针储存下标。

我们将两个数组用下标来对应起来也可以达到指针的效果,

只需要用一个数组用来存储数据,新数组在同下标位置记录下一个元素所在的下标,

这样用数组描述的链表也被称为静态链表。


注意:下文为了方便描述,我将记录下一个结点地址(下标)的变量都统称为了指针


一些术语


结点:链表上的某个数据元素我们称为结点。

头指针:链表中第一个结点的存储位置叫做头指针。整个链表的存取必须从头指针开始进行。


实现方法


1 .创建变量


const int N=1e6+10; 
 int e[N];    存储结点的值。
 int ne[N];   存储结点的指针(即下一个结点的下标)。
 int head;    存储链表头,即头指针。
 int idx;     表示当前用到了哪个结点。


关于 head

刚开始的时候它负责指向空结点,在链表里有元素的时候,它变成了一个指向第一个元素的指针。


2 .初始操作

初始化
head=-1;    表示链表没有内容。
idx=0;      链表中还没有元素,故从零开始。


最开始的时候,链表的头节点要指向-1,

为的就是在后面进行不断操作后仍然可以知道链表是在什么时候结束。

虽然我们在进行各种操作的时候,元素所在的下标看上去很乱,

但是当我们访问的时候,是靠着指针,也就是靠ne[ ]来访问的,

这样即便下标乱,也与我们要做的事不相关了。

另外,我们遍历链表的时候也是这样,靠的是ne[ ]。


3 .插入操作


链表的插入有两种分别是①头结点插入和②链表中间插入。


①头结点插入。


void int_to_head(int x){//和链表中间插入的区别就在于它有head头节点
    e[idx] = x;       第一步,先将值放进去
    ne[idx] = head;   head作为一个指针指向空节点,现在ne[idx] = head;做这把交椅的人换了
                      先在只是做到了第一步,将元素x的指针指向了head原本指向的
    head = idx;       head现在表示指向第一个元素了,它不在是空指针了。(不指向空气了)
    idx ++;           结点向下移一位,为下一次插入元素做准备。
}


②将x插入到下标为k的点的后面。


void add(int k, int x){
    e[idx] = x;         先将元素插进去
    ne[idx] = ne[k];    让元素x配套的指针,指向它要占位的元素的下一个位置
    ne[k] = idx;        让原来元素的指针指向自己
    idx ++; }           将idx向后挪


所采用的思路就是


将x的指针指向 k 指针指向的结点,也就是 k 的下一个结点。

即 ne[idx] = ne[k]; 现在x的后面就是曾经是 k 后面的结点了。


然后改变k的指针,将其指向指向x

即 ne[k] = idx; 现在 k的后面就是x了。


4 .删除操作


将下标是k的后一个结点删掉


void remove(int k){
    ne[k] = ne[ne[k]];  让k的指针指向,k下一个人的下一个人,那中间的那位就被挤掉了。
}


实现删除操作的思路也很妙.

其实我们没必要真的删除储存在e[ ]的数据,而是需要对ne[ ]下文章。

只需要将ne[ ]下标为 k 的指针指向原本指向的结点的下一个就可以了。

这种 k 原本的下一个结点就被越过去, k 直接指向了下下个,而遍历是靠指针ne[ ]来做的,所以k 的下一个结点永远不会被遍历到,这样就达到了删除的效果。


5 .链表遍历


for(int i=head;i!=-1;i=ne[i])
cout<<e[i];


通过指针数组ne[ ]来遍历,应该最开始head指向的 -1 。

经过数据不断的插入,-1 仍然是最后一个结点(此节点不存贮具体数据,仅用于链表遍历)。

所以只要指针不等于 -1 就说明链表还没遍历完。


算法模板


const int N=1e6+10;
int head, e[N], ne[N], idx;
// 初始化
void init()  
{ head = -1;  idx = 0;}
// 在链表头插入一个数a
void head_add(int a)
{
    e[idx] = a,ne[idx] = head, head = idx ++ ;
}
//在k的后面插入x(k!=0)
void add(int k, int x)
{
    e[idx] = x;//先将元素插进去
    ne[idx] = ne[k];//让元素x配套的指针,指向它要占位的元素的下一个位置
    ne[k] = idx;//让原来元素的指针指向自己
    idx ++;//将idx向后挪
}
//将其他节点删除(k!=0)
void Delete(int k)
{
  n[k]=ne[ne[k]];
}
//链表遍历
for(int i=head;i!=;i=ne[i])
cout<<e[i];


完结散花


ok以上就是对模拟数据结构之链表的全部讲解啦,很感谢你能看到这儿啦。如果有遗漏、错误或者有更加通俗易懂的讲解,欢迎小伙伴私信我,我后期再补充完善。


参考文章


[1]程杰.大话数据结构[M].北京:清华大学出版社 ,2011:5-14 .


https://www.acwing.com/solution/content/16251/


全部模板链接


https://www.acwing.com/blog/content/404


相关文章
|
1月前
|
存储 算法 Perl
数据结构实验之链表
本实验旨在掌握线性表中元素的前驱、后续概念及链表的建立、插入、删除等算法,并分析时间复杂度,理解链表特点。实验内容包括循环链表应用(约瑟夫回环问题)、删除单链表中重复节点及双向循环链表的设计与实现。通过编程实践,加深对链表数据结构的理解和应用能力。
57 4
|
2月前
|
算法 索引
❤️算法笔记❤️-(每日一刷-141、环形链表)
❤️算法笔记❤️-(每日一刷-141、环形链表)
53 0
|
2月前
|
算法
【❤️算法笔记❤️】-(每日一刷-876、单链表的中点)
【❤️算法笔记❤️】-(每日一刷-876、单链表的中点)
53 0
|
27天前
|
存储 缓存 算法
在C语言中,数据结构是构建高效程序的基石。本文探讨了数组、链表、栈、队列、树和图等常见数据结构的特点、应用及实现方式
在C语言中,数据结构是构建高效程序的基石。本文探讨了数组、链表、栈、队列、树和图等常见数据结构的特点、应用及实现方式,强调了合理选择数据结构的重要性,并通过案例分析展示了其在实际项目中的应用,旨在帮助读者提升编程能力。
52 5
|
1月前
|
存储 缓存 算法
通过优化算法和代码结构来提升易语言程序的执行效率
通过优化算法和代码结构来提升易语言程序的执行效率
|
1月前
|
存储 C语言
【数据结构】手把手教你单链表(c语言)(附源码)
本文介绍了单链表的基本概念、结构定义及其实现方法。单链表是一种内存地址不连续但逻辑顺序连续的数据结构,每个节点包含数据域和指针域。文章详细讲解了单链表的常见操作,如头插、尾插、头删、尾删、查找、指定位置插入和删除等,并提供了完整的C语言代码示例。通过学习单链表,可以更好地理解数据结构的底层逻辑,提高编程能力。
93 4
|
1月前
|
算法 测试技术 开发者
在Python开发中,性能优化和代码审查至关重要。性能优化通过改进代码结构和算法提高程序运行速度,减少资源消耗
在Python开发中,性能优化和代码审查至关重要。性能优化通过改进代码结构和算法提高程序运行速度,减少资源消耗;代码审查通过检查源代码发现潜在问题,提高代码质量和团队协作效率。本文介绍了一些实用的技巧和工具,帮助开发者提升开发效率。
47 3
|
1月前
|
算法 安全 搜索推荐
2024重生之回溯数据结构与算法系列学习之单双链表精题详解(9)【无论是王道考研人还是IKUN都能包会的;不然别给我家鸽鸽丢脸好嘛?】
数据结构王道第2.3章之IKUN和I原达人之数据结构与算法系列学习x单双链表精题详解、数据结构、C++、排序算法、java、动态规划你个小黑子;这都学不会;能不能不要给我家鸽鸽丢脸啊~除了会黑我家鸽鸽还会干嘛?!!!
|
1月前
|
存储 Web App开发 算法
2024重生之回溯数据结构与算法系列学习之单双链表【无论是王道考研人还是IKUN都能包会的;不然别给我家鸽鸽丢脸好嘛?】
数据结构之单双链表按位、值查找;[前后]插入;删除指定节点;求表长、静态链表等代码及具体思路详解步骤;举例说明、注意点及常见报错问题所对应的解决方法
|
1月前
|
算法
数据结构之购物车系统(链表和栈)
本文介绍了基于链表和栈的购物车系统的设计与实现。该系统通过命令行界面提供商品管理、购物车查看、结算等功能,支持用户便捷地管理购物清单。核心代码定义了商品、购物车商品节点和购物车的数据结构,并实现了添加、删除商品、查看购物车内容及结算等操作。算法分析显示,系统在处理小规模购物车时表现良好,但在大规模购物车操作下可能存在性能瓶颈。
49 0