一、链表的定义
线性表的链式存储就是链表。
它是将元素存储在物理上任意的存储单元中,由于⽆法像顺序表⼀样通过下标保证数据元素之间的逻辑关系,链式存储除了要保存数据元素外,还需额外维护数据元素之间的逻辑关系,这两部分信息合称结点(node)。即结点有两个域:保存数据元素的数据域和存储逻辑关系的指针域。
二、定义-创建-初始化
1. 两个⾜够⼤的数组,⼀个⽤来存数据,⼀个⽤来存下⼀个结点的位置
2. 变量h ,充当头指针,表⽰头结点的位置
3. 变量id ,为新来的结点分位置
const int N = 1e5 + 10;
int h; // 头指针
int id; // 下⼀个元素分配的位置
int e[N], ne[N]; // 数据域和指针域
// 下标 0 位置作为哨兵位
// 其中 ne 数组全部初始化为 0,其中 ne[i] = 0 就表⽰空指针,后续没有结点
三、头插
// 头插
void push_front(int x)
{
// 先把 x 放在⼀个格⼦⾥⾯
id++;
e[id] = x;
// 修改指针,顺序不能颠倒!
// 1. x 的右指针指向哨兵位的后继
// 2. 哨兵位的右指针指向 x
ne[id] = ne[h];
ne[h] = id;
}
四、遍历链表
// 打印链表
void print()
{
// 定义⼀个指针从头结点开始
// 通过 ne 数组逐渐向后移动
// 直到遇到空指针
for(int i = ne[h]; i; i = ne[i])
{
cout << e[i] << " ";
}
cout << endl << endl;
}
通过i是否等于0来判断。
五、按值查找
解法⼀:遍历整个链表即可。
解法⼆:如果存储的值数据范围不⼤,可以⽤哈希表优化。(不知道哈希表是什么没有关系,只要知道mp数组的作⽤,把它当成⼀个标记数组即可。)
int mp[N]; // mp[i] 表⽰ i 在这个元素存放的位置
/*
push_front 和 insert 的时候,打上标记
mp[x] = id; // x 这个元素存放的位置是 id
erase 的时候,消除标记
mp[x] = 0;
*/
// 查询值为 x 的元素存储的位置
int find(int x) // 注意,这⾥传⼊的是元素的值
{
// 策略⼀:先找到 x,然后返回 x 后⾯的元素
for(int i = ne[h]; i; i = ne[i]) // 遍历链表
{
if(e[i] == x) // 如果找到 x
{
return i;
}
}
// 找不到就返回 0
return 0;
// // 策略⼆:使⽤哈希表优化
return mp[x]; // mp[x] ⾥⾯就存着 x 这个元素存储的位置下标
}
如果运用map操作,那么在头插中应该这样写
id++;
e[id] = x;
mp[x] = id;
ne[id] = ne[h];
ne[h] = id;
六、在任意位置之后插⼊元素
// 在存储位置为 p 的元素后⾯,插⼊⼀个元素 x
void insert(int p, int x) // ⼀定要注意,这⾥的 p 是位置,不是元素
{
id++; // x 这个元素分配的位置
e[id] = x; // 将 x 放在 id 位置处
ne[id] = ne[p]; // x 指向 p 的后⾯
ne[p] = id; // p 指向 x
}
和头插操作差不多。
就是把ne[h]换成了ne[p]。
若使用mp数组,应该
id++;
e[id] = x;
mp[x] = id;
ne[id] = ne[p];
ne[p] = id;
七、删除任意位置之后的元素
// 删除存储位置为 p 后⾯的元素
void erase(int p) // 注意 p 表⽰元素的位置
{
if(ne[p])
{
mp[e[ne[p]]] = 0; // 将 p 后⾯的元素从 mp 中删除
ne[p] = ne[ne[p]]; // 指向下⼀个元素的下⼀个元素
}
}
所有测试代码:
#include <iostream>
using namespace std;
const int N = 1e5 + 10;
// 创建
int e[N], ne[N], h, id;
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; // 标记 x 存储的位置
// 先让新结点指向头结点的下⼀个位置
// 然后让头结点指向新来的结点
ne[id] = ne[h];
ne[h] = id;
}
// 按值查找
int find(int x)
{
// // 解法⼀:遍历链表
// for(int i = ne[h]; i; i = ne[i])
// {
// if(e[i] == x) return i;
// }
// return 0;
// 解法⼆:⽤ mp 数组优化
return mp[x];
}
// 在任意位置 p 之后,插⼊⼀个新的元素 x
void insert(int p, int x)
{
id++;
e[id] = x;
mp[x] = id;
ne[id] = ne[p];
ne[p] = id;
}
// 删除任意位置 p 后⾯的元素
void erase(int p)
{
if(ne[p]) // 当 p 不是最后⼀个元素的时候
{
mp[e[ne[p]]] = 0; // 把标记清空
ne[p] = ne[ne[p]];
}
}
int main()
{
for(int i = 1; i <= 5; i++)
{
push_front(i);
print();
}
//cout << find(1) << endl;
//cout << find(5) << endl;
//cout << find(6) << endl;
// insert(1, 10);
// print();
// insert(2, 100);
// print();
erase(2);
print();
erase(4);
print();
return 0;
}