数据结构漫游记:静态链表的实现(CPP)

嘿,各位技术潮人!好久不见甚是想念。生活就像一场奇妙冒险,而编程就是那把超酷的万能钥匙。此刻,阳光洒在键盘上,灵感在指尖跳跃,让我们抛开一切束缚,给平淡日子加点料,注入满满的passion。准备好和我一起冲进代码的奇幻宇宙了吗?Let's go!

我的博客:yuanManGan

我的专栏:C++入门小馆 C言雅韵集 数据结构漫游记 闲言碎语小记坊

之前我们了解到动态链表的实现感兴趣的可以点点链接看一看

数据结构漫游记:动态链表的实现-CSDN博客

静态链表的认识

我们知道链表有两部分组成

一部分是数据域

一部分是指针域

我们动态实现链表时每次都会为新的链表申请空间,会搞得很麻烦代码量也很高,运行的时间也会很高,但静态实现完美的解决了这个问题。

我们是不是可以用空间很大的数组来分开存储指针和指针呢?

用数组的下标来找到每个结点。

静态链表的实现

定义两个数组分别为e和ne存储数据和指针。再用h指向哨兵位(指第一个不存储任何有效数据的结点),id指向最后一个结点。

这是有人就会问了你这个ne数组是不是多余了,我靠一个一个下标就能找到下一个元素啊,根本用不到ne数组啊

小伙子你想的太天真了,那你来实现下面这个呢?

如果你画出来这个结构会发现是A-》D-》E,C去哪里啦?每次C是无效数据,可能被删除了

创建空链表

cpp 复制代码
const int N = 1e5 + 10;

int ne[N], e[N]; // 创建两个很大的数组

int id, h;

int main()
{

    return 0;
} 

注意这里将数组放在全局变量有两个好处,一个是全局变量的内存更大,溢出问题更少出现;另一个是数组等变量不初始化放在main函数外会自动初始化为0。

这就实现了第一步创建空链表。

头插

无论是头插还是什么基本的思路都和动态的链表无二。我们需要先将新结点指向下一个结点,再将哨兵位的指针指向新节点。顺序不能搞错,不然就不能找到下一个结点了。

代码实现:

cpp 复制代码
void push_front(int x)
{
    id++;//指向下一个空的位置
    
    //处理新结点
    e[id] = x;//存储数据
    ne[id] = ne[h];
    
    //处理哨兵位
    ne[h] = id;
}

遍历链表打印

遍历链表的思路就是,定义一个指针cur令cur =necur直到cur等于0就结束了。也和动态链表的思路大差不差。

实现:

cpp 复制代码
void print()
{
    for (int i = ne[id]; i; i = ne[i])
    {
        cout << e[i] << " ->";
    }
    cout << endl << endl;
}

按值查找

实现按值查找一种方法就是遍历链表然后一个一个找,但这种做法时间复杂度是O(N)。这里我们可以先创建一个mp数组,以存储的数据为下标存储每个元素的地址。

cpp 复制代码
const int N = 1e5 + 10;

int ne[N], e[N]; // 创建两个很大的数组

int id, h;

int mp[N];

int main()
{

    return 0;
} 

然后在插入元素时储存元素的下标

cpp 复制代码
void push_front(int x)
{
    id++;//指向下一个空的位置
    
    //处理新结点
    e[id] = x;//存储数据
    ne[id] = ne[h];
    
    //处理哨兵位
    ne[h] = id;
    //标记新结点的位置
    mp[x] = id;
}

就可以实现find函数了

cpp 复制代码
int find(int x)
{
    /*int i = ne[h];
    while (i)
    {
	    if (e[i] == x) return i;
	    i = ne[i];
    }
    return 0;*/

    return mp[x];
}

注释部分是第一种方法。

这里有一个弊端,当存储两个相同的数时,mp数组就不知道存储谁的下标,当没有重复的数据时使用这个方法的时间复杂度是O(1),大大的优化。

在任意位置之后处插入数据

这个功能要传入的数据是某个位置的下标,如果传入的是数据,也得转化为下标来计算,这个位置之后的插入就和头插的执行过程差不多,这里就简单带过咯

cpp 复制代码
void push_front(int p,int x)
{
    id++;//指向下一个空的位置
    
    //处理新结点
    e[id] = x;//存储数据
    ne[id] = ne[p];
    
    //处理p位置
    ne[p] = id;
    //标记新结点
    mp[x] = id;
}

这里就是把h换成了p。

删除任意位后的数据

删除下一个位置就是将这个位置的下一个指针指向下下个元素。

实现也很简单

cpp 复制代码
void erase(int p)
{
    if(ne[p]) // 当 p 不是最后一个元素的时候
    {
        mp[e[ne[p]]] = 0; // 把标记清空

        ne[p] = ne[ne[p]];
    }
}

注意这里的p不能是最后一个元素哦!

这里不实现尾插,在任意位置之前插入,删除任意位置的原因很简单,相对与这些代码要复杂一点,并且时间复杂度都基本上是O(n),我们可以使用其他的数据结构来实现。

比如下期我要写的循环链表,请大家敬请期待!谢谢! ~

相关推荐
我命由我123456 小时前
LRU 缓存淘汰策略
数据结构·redis·学习·mysql·算法·缓存·学习方法
垆边人似月.6 小时前
华为题:合并区间
数据结构·c++·算法
垆边人似月.6 小时前
华为题:滑动窗口最大值
数据结构·c++·算法
m0_547486667 小时前
《数据结构 (C语言)》期末试卷及答案(10套)2026版
c语言·开发语言·数据结构
杜 硕7 小时前
归并排序详解
数据结构·算法·排序算法
_wxd6669 小时前
二叉搜索树(BST)万字详解:概念、操作、性能分析与 C++ 实现
数据结构·c++·二叉搜索树
Logic1019 小时前
C语言/数据结构动态规划题解:字符串变回文的最少操作次数——最长回文子序列(LPS)
c语言·数据结构·算法·动态规划·时间复杂度·回文串·最长回文子序列
Logic10110 小时前
C语言/数据结构组合数学题解:数字好排列计数——质数倍数分类与阶乘取模
c语言·数据结构·算法·组合数学·时间复杂度·排列·取模运算
longlongzihan11 小时前
LeetCode 25. K 个一组翻转链表 (迭代+分组反转法)
算法·leetcode·链表
沫璃染墨11 小时前
《从零入门数据结构与算法(二):线性表详解——从顺序表原理到静态与动态实现》
c语言·数据结构·顺序表