在一些其他平台上刷题遇到的单链表一般是下面这样子的
struct Node{
int val;
Node* next;
};
我们为什么还需要用数组去模拟单链表呢,不是多次一举嘛。实际上,用上面一种方式,当数据量很大的时候,会用到new 去 在系统中申请空间,这个就会直接超出题目限制条件(这里说的是算法题目)。在工程中,不用去new 那么多节点,所以直接用上面的这种方式就行。所以在这里讲解数组去实现单链表。
单链表
一般在邻接表中使用
用途: 1. 存储图 和 树
图解: e 数组存储链表节点中的值, ne 数组存储的节点中next指针, 空节点的next 规定为-1.
相关题目:
实现一个单链表,链表初始为空,支持三种操作:
向链表头插入一个数;
删除第 k 个插入的数后面的数;
在第 k 个插入的数后插入一个数。
现在要对该链表进行 M 次操作,进行完所有操作后,从头到尾输出整个链表。
注意:题目中第 k 个插入的数并不是指当前链表的第 k 个数。例如操作过程中一共插入了 n 个数,则按照插入的时间顺序,这 n 个数依次为:第 1 个插入的数,第 2 个插入的数,…第 n 个插入的数。
输入格式
第一行包含整数 M,表示操作次数。
接下来 M 行,每行包含一个操作命令,操作命令可能为以下几种:
H x,表示向链表头插入一个数 x。
D k,表示删除第 k 个插入的数后面的数(当 k 为 0 时,表示删除头结点)。
I k x,表示在第 k 个插入的数后面插入一个数 x(此操作中 k 均大于 0)。
输出格式
共一行,将整个链表从头到尾输出。
数据范围
1≤M≤100000
所有操作保证合法。
输入样例:
10
H 9
I 1 1
D 1
D 0
H 6
I 3 6
I 4 5
I 4 5
I 3 4
D 6
输出样例:
6 4 6 5
实现思路:
- 初始化 (对应下面 init 函数)
- 头结点插入操作 (对应下面 int_to_head 函数)
- 指定节点插入 (对应下面 add 函数)
- 删除节点操作 (对应下面 remove函数)
code实现:
#include <iostream>
using namespace std;
const int N = 100010;
int n;
int h[N], e[N], ne[N], head, idx;
//对链表进行初始化
void init(){
head = -1;//最开始的时候,链表的头节点要指向-1,
//为的就是在后面进行不断操作后仍然可以知道链表是在什么时候结束
/*
插句题外话,我个人认为head其实就是一个指针,是一个特殊的指针罢了。
刚开始的时候它负责指向空结点,在链表里有元素的时候,它变成了一个指向第一个元素的指针
当它在初始化的时候指向-1,来表示链表离没有内容。
*/
idx = 0;//idx在我看来扮演两个角色,第一个是在一开始的时候,作为链表的下标,让我们好找
//第二在链表进行各种插入,删除等操作的时候,作为一个临时的辅助性的所要操作的元素的下
//标来帮助操作。并且是在每一次插入操作的时候,给插入元素一个下标,给他一个窝,感动!
/*
再次插句话,虽然我们在进行各种操作的时候,元素所在的下标看上去很乱,但是当我们访问的
时候,是靠着指针,也就是靠ne[]来访问的,这样下标乱,也就我们要做的事不相关了。
另外,我们遍历链表的时候也是这样,靠的是ne[]
*/
}
//将x插入到头节点上
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向后挪
/*
为了将这个过程更好的理解,现在
将指针转变的这个过程用比喻描述一下,牛顿老师为了省事,想插个队,队里有两个熟人
张三和李四,所以,他想插到两个人中间,但是三个人平时关系太好了,只要在一起,就
要让后面的人的手插到前面的人的屁兜里。如果前面的人屁兜里没有基佬的手,将浑身不
适。所以,必须保证前面的人屁兜里有一只手。(张三在前,李四在后)
这个时候,牛顿大步向前,将自己的手轻轻的放入张三的屁兜里,(这是第一步)
然后,将李四放在张三屁兜里的手抽出来放到自己屁兜里。(这是第二步)
经过这一顿骚操作,三个人都同时感觉到了来自灵魂的战栗,打了个哆嗦。
*/
}
//将下标是k的点后面的点个删掉
void remove(int k){
ne[k] = ne[ne[k]]; // 这个容易理解
}
int main(){
cin >> n;
init();//初始化
for (int i = 0; i < n; i ++ ) {
char s;
cin >> s;
if (s == 'H') {
int x;
cin >> x;
int_to_head(x);
}
if (s == 'D'){
int k;
cin >> k;
if (k == 0) head = ne[head];//删除头节点
remove(k - 1);//因为是从0开始的,这里删除的时候需要 - 1
}
if (s == 'I'){
int k, x;
cin >> k >> x;
add(k - 1, x);//因为是从0开始的,这里删除的时候需要 - 1
}
}
for (int i = head; i != -1; i = ne[i]) cout << e[i] << ' ' ;
cout << endl;
return 0;
}