链表题
1、用基本逻辑结构实现链表:
最简单的方式就是一个value和一个next指针,构成一个简单的单链表。但是在工作中不推荐使用,因为每次调用该结构都需要重新初始化一个链表,这将极大的影响算法的速度,可以提前初始化一定数量的链表,来防止速度方面问题的影响。
2、用数组模拟链表:
单链表最大的功能就是邻接表,作为存储图和树的基本结构。单链表可以用来写邻接表(包括n个链表),邻接表可以存储树和图,最短路问题、最小生成树问题、最大流问题都可以用邻接表来存储。
双链表的功能是优化某些问题。大话数据结构写到,为了克服单向性的弱点(如在单链表中国查找要从头开始,如果查的元素是最后一个,会极大的浪费时间),双向链表被设计出来,和单链表的区别是多了一个前驱指针。本质上是用空间来换时间,为某个结点的前后操作带来了便利。双向链表的缺点是,指针数的增加会导致存储空间需求增加;添加和删除数据时需要改变更多指针的指向。
单链表,存储的值为3、5、7、9,对于的next指针指向的元素位置为1,2,3,-1
\826. 单链表
题目,单链表
实现一个单链表,链表初始为空,支持三种操作:
-
向链表头插入一个数;
-
删除第 k个插入的数后面的一个数;
-
在第 k 个插入的数后插入一个数。
现在要对该链表进行 M次操作,进行完所有操作后,从头到尾输出整个链表。
注意:题目中第 k 个插入的数并不是指当前链表的第 k个数。例如操作过程中一共插入了 n个数,则按照插入的时间顺序,这 n 个数依次为:第 11 个插入的数,第 22 个插入的数,…第 n个插入的数。
输入格式
第一行包含整数 M,表示操作次数。
接下来 M 行,每行包含一个操作命令,操作命令可能为以下几种:
-
H x
,表示向链表头插入一个数 x。 -
D k
,表示删除第 k 个插入的数后面的数(当 k 为 0 时,表示删除头结点)。 -
I k x
,表示在第 k 个插入的数后面插入一个数 x(此操作中 k 均大于 0)。
输出格式
共一行,将整个链表从头到尾输出。
数据范围
1≤M≤10000 所有操作保证合法。
输入样例:
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
C语言
#include <stdio.h> #define N 100010 int n; int h[N], e[N], ne[N], head, idx; void init(){ head = -1; idx = 0; } void int_to_head(int x) { e[idx] = x; ne[idx] = head; head = idx; idx ++; } void add(int k, int x) { e[idx] = x; ne[idx] = ne[k]; ne[k] = idx; idx ++; } void removek(int k) { if (k == 0) head = ne[head]; else if (ne[k] != -1) ne[k] = ne[ne[k]]; } int main() { init(); scanf("%d", &n); char s; while(n--) { scanf(" %c", &s); if (s == 'H') {//在链表头插入 int x; scanf("%d", &x); if (head == -1) {//判断是否为空 head = idx; e[idx] = x; ne[idx] = -1; idx ++; } else { int_to_head(x); } } if (s == 'D') {//删除 int k; scanf("%d", &k); removek(k - 1);//处理删除第k个数后面的数 } if (s == 'I') {//插入 int k, x; scanf("%d %d", &k, &x); add(k - 1, x);//处理插入第k个数后面 } scanf("%c", &s);//读入换行符并丢弃 } int i = head; while(i != -1) { printf("%d ", e[i]); i = ne[i]; } printf("\n"); return 0; }
c++语言实现
#include <iostream> using namespace std; const int N = 100010; // head 表示头结点的下标 // e[i] 表示节点i的值 // ne[i] 表示节点i的next指针是多少 // idx 存储当前已经用到了哪个点 int head, e[N], ne[N], idx; // 初始化 void init() { head = -1; idx = 0; } // 将x插到头结点 void add_to_head(int x) { e[idx] = x, ne[idx] = head, head = idx ++ ; } // 将x插到下标是k的点后面 void add(int k, int x) { e[idx] = x, ne[idx] = ne[k], ne[k] = idx ++ ; } // 将下标是k的点后面的点删掉 void remove(int k) { ne[k] = ne[ne[k]]; } int main() { int m; cin >> m; init(); while (m -- ) { int k, x; char op; cin >> op; if (op == 'H') { cin >> x; add_to_head(x); } else if (op == 'D') { cin >> k; if (!k) head = ne[head]; else remove(k - 1); } else { cin >> k >> x; add(k - 1, x); } } for (int i = head; i != -1; i = ne[i]) cout << e[i] << ' '; cout << endl; return 0; }
疑问:
1、因为题干:注意:题目中第 k 个插入的数并不是指当前链表的第 k个数。例如操作过程中一共插入了 n个数,则按照插入的时间顺序,这 n 个数依次为:第 11 个插入的数,第 22 个插入的数,…第 n个插入的数。所以c语言写出和c++写出的运行结果不一样,c++是大佬写的,根据模拟数据
输入样例:
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
c语言输出的应该是6 4 6 5 1
c++输出的是6 4 6 5
根据我思考的逻辑
1、10
k序号0
2、H 9 插入头结点,9的k序号为1
9 10
1 0
3、I 1 1 把1插入到k序号为1的元素后面
9 1 10
1 2 0
4、D 1 删除k序号为1的元素
1 10
2 0
5、D 0 删除k序号为0的元素
1
2
6、H 6 头插值为6的元素,k序号为3
6 1
3 2
7、I 3 6 插入到k序号为3的元素后面,值为6
6 6 1
4 3 2
8、I 4 5插入到k序号为4的元素后面 ,值为5
6 5 6 1
4 5 3 2
9、I 4 5 插入到k序号为4的元素后面,值为5
6 5 5 6
4 6 5 3 2
10、I 3 4 插入到k序号为3的元素后面,值为4
6 5 5 6 4 1
4 6 5 3 7 2
11、D 6 删除k序号为6的元素
6 5 6 4 1
4 5 3 7 2