数组模拟单链表 ← 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

相关推荐
Alfred king14 小时前
面试150 搜索二维矩阵
线性代数·矩阵·二分查找·数组
Alfred king15 小时前
面试150 IPO
面试·职场和发展·贪心·数组··排序
Alfred king2 天前
面试150 建立四叉树
矩阵··数组·分治
Alfred king4 天前
面试150 环形子数组的最大和
面试·职场和发展·数组·队列·分治
Alfred king23 天前
面试150 生命游戏
leetcode·游戏·面试·数组
Tee xm2 个月前
算法修仙传 第一章 灵根觉醒:数组基础与遍历
java·数据结构·算法·数组·遍历
小学生的信奥之路2 个月前
力扣1991:找到数组的中间位置(前缀和)
数据结构·算法·leetcode·前缀和·数组
Tisfy3 个月前
LeetCode 1550.存在连续三个奇数的数组:遍历
算法·leetcode·题解·数组·遍历
寒山李白3 个月前
JavaSE核心知识点01基础语法01-04(数组)
java·学习·数组
编程火箭车3 个月前
用手机相册教我数组概念——照片分类术[特殊字符][特殊字符]
数据结构·java基础·数组·编程入门·array·数组初始化·照片管理