2022-11-09 Acwing每日一题

本系列所有题目均为Acwing课的内容,发表博客既是为了学习总结,加深自己的印象,同时也是为了以后回过头来看时,不会感叹虚度光阴罢了,因此如果出现错误,欢迎大家能够指出错误,我会认真改正的。同时也希望文章能够让你有所收获,与君共勉!

今天来学习一下用数组模拟链表,又称链式前向星,这里主要介绍单链表与双向链表。

单链表

实现一个单链表,链表初始为空,支持三种操作:

  1. 向链表头插入一个数;
  2. 删除第 k 个插入的数后面的数;
  3. 在第 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

算法原理

  1. 定义存储数组以及指针数组,存储数组是存储数字,可视为节点里的数据域,而指针数组是存放存储数组中的某一个元素的下一个位置,可视为结点里面的指针域,最后定义链表头head与指针idx
  2. 初始化头节点head,初始值为-1(遍历链表的终止条件),初始化指针idx,每在idx处插入一个数字就想后一位。
  3. 选择头插法/第k个节点处插入/删除第k个节点(看代码)。

代码实现

需要强调的是,下标从0就开始存储的单向链表,在第k个位置处插入应表示为k-1

#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 1000010;
int e[N],ne[N],idx,head;


int main()
{
    int n;
    cin >> n;
    idx = 0;
    head = -1;
    
    /*1.主要是k的索引,从0开始所以k都要减1,2.再删除的时候要判断是否删除头节点0,如果是就要让head向后一位*/
    while(n--){
        char c;
        int k,x;
        cin >> c;
        if(c == 'H'){	// 头插
	    cin >> x;
	    e[idx] = x;
	    ne[idx] = head;	// 让idx的下一个节点是head
	    head = idx++; // 再让idx成为head,并且将idx移到下一位
        }
        else if(c == 'D'){	// 删除第k个插入的数
            cin >> k;
            if(!k) head = ne[head];	// 如果删除头节点就移动头节点至下一位
            else ne[k-1] = ne[ne[k-1]];	// 否则就删除第k位
        }
        else{
            cin >> k >> x;
            e[idx] =x;
            ne[idx] = ne[k-1];		// idx位的下一位指向第k位,第k-1位的下一位指向idx
            ne[k-1] = idx++;	// 让idx增加
        }
    }
    
    for(int i=head ; i != -1; i = ne[i])    cout << e[i] << ' ';	// 当i为-1说明到达了最后的节点,停止循环
    return 0;
}

双向链表

实现一个双链表,双链表初始为空,支持 5 种操作:

  1. 在最左侧插入一个数;
  2. 在最右侧插入一个数;
  3. 将第 k 个插入的数删除;
  4. 在第 k 个插入的数左侧插入一个数;
  5. 在第 k 个插入的数右侧插入一个数
    现在要对该链表进行 M 次操作,进行完所有操作后,从左到右输出整个链表。

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

输入格式
第一行包含整数 M,表示操作次数。

接下来 M 行,每行包含一个操作命令,操作命令可能为以下几种:

L x,表示在链表的最左端插入数 x。
R x,表示在链表的最右端插入数 x。
D k,表示将第 k 个插入的数删除。
IL k x,表示在第 k 个插入的数左侧插入一个数。
IR k x,表示在第 k 个插入的数右侧插入一个数。
输出格式
共一行,将整个链表从左到右输出。

数据范围
1≤M≤100000
所有操作保证合法。

输入样例:
10
R 7
D 1
L 3
IL 2 10
D 3
IL 2 7
L 8
R 9
IL 4 7
IR 2 2
输出样例:
8 7 7 3 2 9

算法原理

1.初始化左右指针数组lr,取两个虚拟节点表示链表的头和尾,头始终是0,尾始终是1,即r[0] = 1l[1] = 0idx因此也是从下标2开始。
2.5个操作细节看代码。

代码实现

需要强调的是,下标从2就开始存储的双向链表,在第k个位置处插入应表示为k-1+2,即k+1

#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
#define N 1000010

int e[N],l[N],r[N],idx;

void init(){ // 初始化
    l[1] = 0,r[0] = 1;	//尾结点1的左边指向头节点下标0,头结点的右边指向尾结点下标1
    idx = 2;	// 从下标为2开始存储
}

void Insert(int k,int x){	// 在第k个数(位置)后面插入
    e[idx] = x;
    l[idx] = k;		// 插入的数的左边是第k个数
    r[idx] = r[k];	// 插入的数右边是第k+1个数。
    l[r[k]] = idx;	// 第k个数的右边为idx
    r[k] = idx++;	// 第k+1个数的左边为idx,让idx增加一位存储下一个插入的数
}

void remove(int k){	// 删除第k个数
    r[l[k]] = r[k];		// 第k个数的左边的数指向第k个数的下一个位置
    l[r[k]] = l[k];		// 第k个数的右边的数指向第k个数的上一个位置
}

int main()
{
    int n ;
    cin >> n;
    init();
    while(n--){
        string s;
        int k,x;
        cin >> s;
        if(s=="L"){		// 头插法,在最左端插入
            cin >> x;
            Insert(0,x);
        }
        else if(s=="R"){	// 尾插法,在最右端插入
            cin >> x;
            Insert(l[1],x);	// 最后一位的下一位插入一个数
        }
        else if(s=="IL"){	// 第k个数的左边插入一个数:注意:下标是从2开始的
            cin >> k >> x;
            Insert(l[k+1],x);	// 从0开始的第k个数应插入的位置为k-1,而从2开始的是k-1+2,左边插入就变成l[k+1]
        }
        else if(s=="IR"){	// 第k个数右边插入一个数
            cin >> k >> x;
            Insert(k+1,x);	// 同理在k+1处插入就是在第k个数后面插入
        }
        else{
            cin >> k;
            remove(k+1);	// 同理,删除位置为k+1其实就是删除第k个数
        }
    }
    for(int i=r[0]; i != 1; i=r[i]) cout << e[i] << " ";	// 注意最右端的虚拟节点位置为1,到1就结束
    cout << endl;
    return 0;
}
posted @ 2022-11-09 15:16  ZmQmZa  阅读(20)  评论(0编辑  收藏  举报