手撕目录
前言
众所周知,其实在企业做项目中,对于链表的使用,更多是偏向于用结构体和指针的动态链表,那为什么在算法竞赛中我们还要学会用数组来模拟链表呢,因为用结构体和指针的话,在oj题时会很慢,很容易TLE,相比之下数组的静态链表就快多了。
单链表
1.原理
首先,我们先来看看链表应该是个什么样子.
很明显,链表要具备两个基本要素,第一个结点,第二个链子。
黑色的表示每个结点储存的值,而红色的表示每个结点的下标。
接下来我们来想想怎么连接每一个结点呢
对,就是用ne数组来储存下一个结点的下标
那么很显然,我们既需要一个数组来储存结点的值,也需要一个结点来储存该结点的下一个结点的下标。
int e[N];//用来储存结点的值
int ne[N];//用来储存该结点的下一个的下标
接下来,我们可以用代码模拟一下第一张图。
e[0] = 3, ne[0] = 1;
e[1] = 1, ne[1] = 2;
e[2] = 2,ne[2] = 3;
e[3] = 5, ne[3] = -1;//-1规定为空指针
当然,我们还需要一个头指针head和一个指向待插入位置的指针idx
int head = -1,idx = 0;
初始化时,链表为空,头指针为空指针,则head = -1,因为链表中什么都没有,待有位置插入,所以idx=0
case 1 :在链表头部插入元素
想要讲元素插入只需要进行如上的操作即可,接下来展示代码
void headinsert(int x)
{
e[idx] = x;//先赋值给要插入的元素
nec[idx] = head;//第一步操作,使该元素的ne数组指向下一个元素的下标
head = idx ++;//第二步操作,使head指向该元素的下标,finally,将idx++
}
case 2 : 在第k个插入的数中后面插入一个数x
note:第k个插入的数可不是链表里的第k个数,是按时间顺序的第k个数
和上一种情况一样就行了
void insert(int k,int x)
{
e[idx] = x, ne[idx] = ne[k - 1], ne[k - 1] = idx ++;
}
case 3 : 删除k后面的那个元素
删除的话,就很简单的,就让该元素的ne数组指向下下个元素的下标就行了
void de(int k)
{
ne[k] = ne[ne[k]];
}
case 4 : 输出整个链表
for(int i = head; i != -1; i = ne[i])//i == -1时即i为空指针时停下
{
cout << e[i];
}
2.模版题
这是acwing上的一道模版题,接下来展示AC代码
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int N = 1e5+10;
int e[N], ne[N], idx = 0, head = -1;
void headinsert(int x)
{
e[idx] = x, ne[idx] = head, head = idx ++;
}
void insert(int k,int x)
{
e[idx] = x, ne[idx] = ne[k], ne[k] = idx ++;
}
void de(int k)
{
ne[k] = ne[ne[k]];
}
int main()
{
int n;
cin >> n;
while(n--)
{
char op;
int x, k;
cin >> op;
if(op == 'H')
{
cin >> x;
headinsert(x);
}
else if (op == 'D')
{
cin >> k;
if (!k) head = ne[head];
else de(k - 1);
}
else if(op == 'I')
{
cin >> k >> x;
insert(k - 1, x);
}
}
for(int i = head; i != -1; i = ne[i])
{
cout << e[i] << ' ';
}
return 0;
}
双链表
1.原理
双链表即是双向而已,与单链表其实差不多
由于是双向,所以每个结点既有左边,又有右边,所以相对于单链表,我们要加一个l[N] (left),r[N] (right)
int e[N], ne[N], l[N], r[N];
int idx = 0;
初始化 :单链表的初始化是head,而双链表是这样的
void unit()
{
l[0] = 1,r[1] = 0;//note:这两个并不属于链表里面
idx = 2;
}
case 1 :在k后面插入一个数x
由于只要掌握了单链表,这个应该就很好理解,所以我们直接上图解和代码。
void insert(int k, int x)
{
e[idx] = x;
l[idx] = k, r[idx] = r[k];
l[r[k]] = idx, r[k] = idx ++ ;
}
case 2 :删除k结点
void de(int k)
{
l[r[k]] = l[k];
r[l[k]] = r[k];
}
2.模版题
这也是acwing上的一道模版题,直接上ac代码
#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
using namespace std;
const int N = 1e5 + 10;
int m;
int e[N], l[N], r[N];
int idx = 0;
void unit()
{
r[0] = 1,l[1] = 0;
idx = 2;
}
void insert(int k, int x)
{
e[idx] = x;
l[idx] = k, r[idx] = r[k];
l[r[k]] = idx, r[k] = idx ++ ;
}
void de(int k)
{
l[r[k]] = l[k];
r[l[k]] = r[k];
}
int main()
{
cin >> m;
unit();
while(m --)
{
string op;
cin >> op;
int k, x;
if (op == "L")
{
cin >> x;
insert(0, x);
}
else if (op == "R")
{
cin >> x;
insert(l[1], x);
}
else if (op == "D")
{
cin >> k;
de(k + 1);
}
else if (op == "IL")
{
cin >> k >> x;
insert(l[k + 1], x);
}
else
{
cin >> k >> x;
insert(k + 1, x);
}
}
for(int i = r[0]; i != 1;i = r[i])
{
cout << e[i] <<" ";
}
return 0;
}