数据结构 双链表的模拟实现

server/2025/2/13 0:33:01/

一、实现方式

依旧采⽤静态实现的⽅式。
双向链表⽆⾮就是在单链表的基础上加上⼀个指向前驱的指针,那就再来⼀个数组,充当指向前驱的指针域即可。

const int N = 1e5 + 10;
int h; // 头结点 
int id; // 下⼀个元素分配的位置 
int e[N]; // 数据域 
int pre[N], ne[N]; // 前后指针域 
// h 默认等于 0,指向的就是哨兵位 
// 此时链表为空,没有任何⼏点,因此 ne[h] = 0

二、头插

// 头插 
void push_front(int x)
{id++;e[id] = x;mp[x] = id; // 存⼀下 x 这个元素的位置 // 左指向哨兵位,右指向哨兵位的下⼀个位置,也就是头结点 pre[id] = h;ne[id] = ne[h];
// 先修改头结点的指针,再修改哨兵位,顺序不能颠倒 pre[ne[h]] = id;ne[h] = id;
}

顺序(方便记忆):

先把x的两个结点分别指向前一个和后一个。

然后把x后面那个左指针指向x。

最后把x前面的右指针指向x。(必须是最后一步)

三、遍历链表

和单链表遍历的方法一样。

// 打印链表 
void print()
{for(int i = ne[h]; i; i = ne[i]){cout << e[i] << " ";}cout << endl << endl;
}

四、按值查找

直接使⽤mp数组优化了。

// 查找元素 x 在链表中存储的位置 
int find(int x)
{// ⽤ mp 优化 return mp[x];

五、在任意位置之后插⼊元素

// 在存储位置为 p 的元素后⾯,插⼊⼀个元素 x 
void insert_back(int p, int x)
{id++;e[id] = x;mp[x] = id; // 存⼀下 x 这个元素的位置 // 先左指向 p,右指向 p 的后继 pre[id] = p;ne[id] = ne[p];// 先让 p 的后继的左指针指向 id // 再让 p 的右指针指向 id pre[ne[p]] = id;ne[p] = id;
}

和头插差不多。

注意思路

六、在任意位置之前插⼊元素

void insert_front(int p, int x)
{id++;e[id] = x;mp[x] = id; // 存⼀下 x 这个元素的位置 // 先左指针指向 p 的前驱,右指针指向 p pre[id] = pre[p];ne[id] = p;// 先让 p 的前驱的右指针指向 id // 再让 p 的左指针指向 id ne[pre[p]] = id;pre[p] = id;
}

顺序(方便记忆):

先把x的左右指针指向p的前驱和p。

然后把p前驱后指针指向id

最后p的前指针指向id。(必须是最后一步)

七、删除任意位置的元素

// 删除下标为 p 的元素 
void erase(int p)
{mp[e[p]] = 0; // 从标记中移除 ne[pre[p]] = ne[p];pre[ne[p]] = pre[p];
}

所有测试代码:

#include <iostream>
using namespace std;
const int N = 1e5 + 10;
// 创建双链表 
int e[N], ne[N], pre[N], id, h;
int mp[N]; // mp[i] 表⽰:i 这个值存储的位置 
// 遍历链表 
void print()
{for(int i = ne[h]; i; i = ne[i]){cout << e[i] << " ";}cout << endl << endl;
}
// 头插 
void push_front(int x)
{id++;e[id] = x;mp[x] = id;// 先修改新来结点的左右指针 pre[id] = h;ne[id] = ne[h];// 修改哨兵位下⼀个结点的左指针 pre[ne[h]] = id;ne[h] = id;
}
int find(int x)
{return mp[x];
}
// 在任意位置 p 的后⾯插⼊新的元素 x 
void insert_back(int p, int x)
{id++;e[id] = x;mp[x] = id;pre[id] = p;ne[id] = ne[p];pre[ne[p]] = id;ne[p] = id;
}
// 在任意位置 p 的前⾯插⼊新的元素 x 
void insert_front(int p, int x)
{id++;e[id] = x;mp[x] = id;pre[id] = pre[p];ne[id] = p;ne[pre[p]] = id;pre[p] = id;
}
// 删除任意位置 p 的元素 
void erase(int p)
{mp[e[p]] = 0; // 把标记清空 ne[pre[p]] = ne[p];pre[ne[p]] = pre[p];
}
int main()
{for(int i = 1; i <= 5; i++){push_front(i);print();}//cout << find(3) << endl;//cout << find(5) << endl;//cout << find(0) << endl;// insert_front(2, 22);// print();// insert_front(3, 33);// print();// insert_front(4, 44);// print();erase(2);print();erase(4);print();return 0;
}


http://www.ppmy.cn/server/167197.html

相关文章

DEEPSEEK与GPT等AI技术在机床数据采集与数字化转型中的应用与影响

随着人工智能&#xff08;AI&#xff09;技术的迅猛发展&#xff0c;深度学习、自然语言处理等先进技术开始广泛应用于各行各业。在制造业尤其是机床行业&#xff0c;AI技术的融合带来了巨大的变革&#xff0c;尤其在机床数据采集与机床数字化方面的应用。本文将探讨DEEPSEEK、…

直接调字典控制器传字典名称和字典Value查具体的字典Label

vehicle.setColour(dictDataService.selectDictLabel("sys_ddc_vehicle_colour", v.getVehicleColour())); /*** 根据字典类型和字典键值查询字典数据信息* * param dictType 字典类型* param dictValue 字典键值* return 字典标签*/ public String selectDictLabel…

17vue3实战-----使用配置文件生成简易页面

17vue3实战-----使用配置文件生成简易页面 1.写在前面2.背景3.实现3.1界面效果3.2新建config配置文件3.3封装组件3.4使用组件 1.写在前面 后台管理系统的开发很简单。无论是用户模块、部门模块、角色模块还是其它模块,界面和业务逻辑都相对比较简单&#xff0c;我会省略这些模…

通过 UserFeedback 创建浮动联系表单的完整指南

在当今数字时代&#xff0c;网站的用户体验至关重要。而让用户能够随时与网站管理员联系&#xff0c;是提升用户体验的重要方式。浮动联系表单作为一种始终可见的联系途径&#xff0c;可以大大提高用户的参与度和网站转化率。本文将为你详细介绍如何通过 UserFeedback 插件在 W…

Java 大视界 -- 5G 与 Java 大数据融合的行业应用与发展趋势(82)

&#x1f496;亲爱的朋友们&#xff0c;热烈欢迎来到 青云交的博客&#xff01;能与诸位在此相逢&#xff0c;我倍感荣幸。在这飞速更迭的时代&#xff0c;我们都渴望一方心灵净土&#xff0c;而 我的博客 正是这样温暖的所在。这里为你呈上趣味与实用兼具的知识&#xff0c;也…

vue2 多页面pdf预览

使用pdfjs-dist预览pdf&#xff0c;实现预加载&#xff0c;滚动条翻页。pdfjs的版本很重要&#xff0c;换了好多版本&#xff0c;终于有一个能用的 node 20.18.1 "pdfjs-dist": "^2.2.228", vue页面代码如下 <template><div v-loading"loa…

uniapp中使用uCharts折线图X轴数据间隔显示

1、先看官网 https://www.ucharts.cn/ 2、设置代码 "xAxisDemo3":function(val, index, opts){if(index % 2 0){return val}else {return }}, 再在数据中引入设置好样式

Kotlin Android 环境搭建

Kotlin Android 环境搭建 引言 随着移动应用的日益普及,Android 开发成为了一个热门的技术领域。Kotlin 作为一种现代的编程语言,因其简洁、安全、互操作性强等特点,被越来越多的开发者所喜爱。本文将详细介绍 Kotlin Android 环境搭建的步骤,帮助您快速上手 Kotlin Andr…