数组模拟单链表 ← C++

【题目来源】
https://www.acwing.com/problem/content/828/

【题目描述】
实现一个单链表,链表初始为空,支持三种操作:

  1. 向链表头插入一个数;
  2. 删除第 k 个插入的数后面的数;
  3. 在第 k 个插入的数后插入一个数。

现在要对该链表进行 M 次操作,进行完所有操作后,从头到尾输出整个链表。

注意:++题目中第 k 个插入的数并不是指当前链表的第 k 个数++ 。例如操作过程中一共插入了 n 个数,则按照插入的时间顺序,这 n 个数依次为:第 1 个插入的数,第 2 个插入的数,...第 n 个插入的数。

【输入格式】
第一行包含整数 M,表示操作次数。
接下来 M 行,每行包含一个操作命令,操作命令可能为以下几种:

  1. H x,表示向链表头插入一个数 x。
  2. D k,表示删除第 k 个插入的数后面的数(当 k 为 0 时,表示删除头结点)。
  3. 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

【算法分析】
本题利用数组模拟实现单链表。利用数组模拟单链表常见的核心操作代码如下所示:

cpp 复制代码
// head   头结点的下标
// e[i]   结点i的值
// ne[i]  结点i的next指针
// idx    当前可用结点的下标,可看做指针

int head, e[N], ne[N], idx;

void init() { //初始化
    head=-1; //head指向空
    idx=0;
}

void add_to_head(int x) { //将x插入到首元结点
    e[idx]=x;
    ne[idx]=head;  // idx指向head指向的结点
    head=idx++;     // head指向idx
}

void add(int k,int x) { //将x插到到下标为k的结点后面
    e[idx]=x;
    ne[idx]=ne[k];
    ne[k]=idx++;
}

void remove(int k) { //将下标为K后面的点删除
    ne[k]=ne[ne[k]];
}

学习过链式前向星(https://blog.csdn.net/hnjzsyjyj/article/details/126474608)的会发现这些模拟单链表的变量及数组很熟悉。这是因为,链式前向星也是用数组模拟单链表,不过是要模拟多个单链表。链式前向星常见的核心操作代码如下所示:
其中:
val[idx] 表示第 idx 条边的权值。
e[idx] 表示第 idx 条边的终点。
ne[idx] 表示与第 idx 条边同起点的最近一次被添加的边的编号。
h[a] 表示以结点 a 为起点的最近一次被添加的边的编号。这个表述是在使用 ne[idx]=h[a] 时,也即使用 h[a]=idx++ 更新 h[a] 之前而言的。要特别注意这个语境。
很明显,h[a] 中存储的是以 a 为起点的所有边中编号最大的那个。且在遍历时,把这条边作为以 a 为起点的所有边中的第一条边进行遍历。即边的遍历顺序与输入顺序恰好是相反的。而网上常见的表述"e[idx] 表示第 idx 条边的终点,ne[idx] 表示与第 idx 条边同起点的**++下一条边++** 的存储位置,h[a] 表示以 a 为起点的++第一条边++的存储位置",是按边的遍历顺序进行表述的。
在上述约定下,则有:
● 链式前向星的核心代码如下:

cpp 复制代码
void add(int a,int b) {
    e[idx]=b,ne[idx]=h[a],h[a]=idx++;
}

如果是有权图,需多设置一个数组 val[] 存储权值。有权图的链式前向星的核心代码如下:

cpp 复制代码
void add(int a,int b,int w) {
    val[idx]=w,e[idx]=b,ne[idx]=h[a],h[a]=idx++;
}

● 基于链式前向星的深度优先搜索(DFS)的核心代码如下:

cpp 复制代码
void dfs(int u) {
    cout<<u<<" ";
    st[u]=true;
    for(int i=h[u]; ~i; i=ne[i]) { //~i; equivalent to i!=-1;
        int j=e[i];
        if(!st[j]) {
            dfs(j);
        }
    }
}

● 基于链式前向星的广度优先搜索(BFS)的核心代码如下:

cpp 复制代码
void bfs(int u) {
    queue<int>q;
    st[u]=true;
    q.push(u);
    while(!q.empty()) {
        int t=q.front();
        q.pop();
        cout<<t<<" ";
        for(int i=h[t]; ~i; i=ne[i]) { //~i; equivalent to i!=-1;
            int j=e[i];
            if(!st[j]) {
                q.push(j);
                st[j]=true; //need to be flagged immediately after being queued
            }
        }
    }
}

【算法代码】

cpp 复制代码
#include <bits/stdc++.h>
using namespace std;

const int N=1e5+5;

// head   头结点的下标
// e[i]   结点i的值
// ne[i]  结点i的next指针
// idx    当前可用结点的下标,可看做指针

int head, e[N], ne[N], idx;

void init() { //初始化
    head=-1; //head指向空
    idx=0;
}

void add_to_head(int x) { //将x插入到首元结点
    e[idx]=x;
    ne[idx]=head;  // idx指向head指向的结点
    head=idx++;     // head指向idx
}

void add(int k,int x) { //将x插到到下标为k的结点后面
    e[idx]=x;
    ne[idx]=ne[k];
    ne[k]=idx++;
}

void remove(int k) { //将下标为K后面的点删除
    ne[k]=ne[ne[k]];
}

int main() {
    int T;
    cin>>T;

    init();
    while(T--) {
        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]; //对删除头结点特判
            remove(k-1);
        } else {
            cin>>k>>x;
            add(k-1,x);
        }
    }

    for(int i=head; i!=-1; i=ne[i]) cout<<e[i]<<" ";

    return 0;
}


/*
in:
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

out:
6 4 6 5
*/ 

【参考文献】
https://www.acwing.com/blog/content/2141/
https://blog.csdn.net/hnjzsyjyj/article/details/126474608

相关推荐
一丝晨光1 天前
不同语言的注释和数组
java·开发语言·javascript·c++·c·注释·数组
IronmanJay6 天前
【LeetCode每日一题】——LCP 51.烹饪料理
数据结构·算法·leetcode·回溯·数组·递归·lcp 51.烹饪料理
小码狐8 天前
力扣【303-区域检索】【数组-C语言】
c语言·学习·leetcode·力扣·数组
请不要叫我菜鸡10 天前
Go基础学习05-数组和切片关系深度解析
java·后端·golang·数组·切片·底层数据结构
IT规划师10 天前
数据结构 - 数组
数据结构·数组
Xiu Yan14 天前
LeetcodeTop100 刷题总结(一)
java·数据结构·算法·链表·矩阵·哈希算法·数组
七折困16 天前
列表、数组排序总结:Collections.sort()、list.sort()、list.stream().sorted()、Arrays.sort()
java·集合·数组·排序
sweetheart7-716 天前
LeetCode54. 螺旋矩阵(2024秋季每日一题 21)
线性代数·矩阵·力扣·数组·每日一题
IT规划师16 天前
C#|.net core 基础 - 扩展数组添加删除性能最好的方法
c#·.netcore·数组