DAY7 链表

题目:acwing826

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

  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
所有操作保证合法。

#include <iostream>
using namespace std;
const int N = 100010;
int head, e[N], ne[N], idx;
void init()
{
    head = -1;
    idx = 0;
}
void add_to_head(int x)
{
    e[idx] = x, ne[idx] = head, head = idx++;
}
void add(int k, int x)
{
    e[idx] = x, ne[idx] = ne[k], ne[k] = idx++;
}
void remove(int k)
{
    ne[k] = ne[ne[k]];
}
int main()
{
    int m;
    cin >> m;
    init();
    while (m--)
    {
        char op;
        int k, x;
        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);//第k个元素对应的索引为k - 1
        }
        else
        {
            cin >> k >> x;
            add(k - 1, x);//第k个元素对应的索引为k - 1
        }
    }
    for (int i = head; i != -1; i = ne[i]) cout << e[i] << " ";
    cout << endl;
}

本题运用的是单链表,单链表顾名思义就是单向的链表。

链表作为一种数据结构,主要由value(数值)和next(指向)构成,next指向的是下一个数的位置,而不是下一个数的数值,这是必须要注意的。

本题中我们用两个数组来模拟单链表,数组e来存放输入的值,数组ne来存放指向下一个数的位置。

1.在初始化阶段,我们设置head=-1,index=0(索引值)

2.函数add_to_head实现向链表头插入一个数,我们正常的将值填入数组e,然后交换该数值与head的索引值,并且让head=idx,意思就是可以通过head来索引到新插入的值,然后再让索引值自增。

3.函数add实现向链表第k位后插入一个数,处理的过程与add_to_head大同小异,但是注意下面调用函数的时候需要写成k-1,这是由于索引值。首先在数组的下一位存入我们输入的值x,然后将x的next指针变为k的next指针,k的next指针指向x,然后使索引值自增。

4.函数remove实现删除链表的一个节点。初看时可能会感到疑惑,一个静态的链表怎么动态的删除一个数值呢?实际上题目说的删除和正常理解的删除有区别。比如说A-->B-->C,如果我们要把B删除,那么指针只需要绕过B就行了,A--(B)-->C,不用真的把B删除。比如说我们要删除A后面的B,那么我们就要把A的next指针指向C,就是原来B的next指针指向的位置。同样由于索引值的问题,在下面调用函数的时候我们需要微调。

5.主体函数的实现,前面就是简单的输入判断,最后的for循环值得注意:

for (int i = head; i != -1; i = ne[i]) cout << e[i] << " ";从head开始索引,更新方式为i=ne[i],就是next指针,i!=-1这是因为边界值。

具体的图解可以看这一篇AcWing 826. 单链表---图解 - AcWing

题目:acwing827

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

  1. 在最左侧插入一个数;
  2. 在最右侧插入一个数;
  3. 将第 k个插入的数删除;
  4. 在第 k个插入的数左侧插入一个数;
  5. 在第 k个插入的数右侧插入一个数

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

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

输入格式

第一行包含整数 M,表示操作次数。

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

  1. L x,表示在链表的最左端插入数 x。
  2. R x,表示在链表的最右端插入数 x。
  3. D k,表示将第 k个插入的数删除。
  4. IL k x,表示在第 k个插入的数左侧插入一个数。
  5. IR k x,表示在第 k个插入的数右侧插入一个数。
输出格式

共一行,将整个链表从左到右输出。

数据范围

1≤M≤100000
所有操作保证合法。

#include<iostream>
#include<algorithm>
using namespace std;
//双链表有两个指针,指向前+指向后
const int N = 1e6 + 10;
int e[N], l[N], r[N], idx;
void init() {
	r[0] = 1, l[1] = 0;
	idx = 2;
}
void add(int k, int x) {
	e[idx] = x;
	r[idx] = r[k];
	l[idx] = k;
	l[r[k]] = idx;
	r[k] = idx;
	idx++;
}
void remove(int k) {
	r[l[k]] = r[k];
	l[r[k]] = l[k];
}
int main() {
	init();
	int M;
	cin >> M;
	string op; int x; int k;
	while (M--) {
		cin >> op;
		if (op == "L") {
			
			cin >> x;
			add(0, x);
		}
		else if (op == "R") {
			cin >> x;
			add(l[1], x);
		}
		else if (op == "D") {
			cin >> k;
			remove(k + 1);
		}
		else if (op == "IL") {
			cin >> k >> x;
			add(l[k + 1], x);
		}
		else {
			cin >> k >> x;
			add(k+1, x);
		}
	}
	for (int i = r[0]; i != 1; i = r[i])cout << e[i] << " ";
	cout << endl;
	return 0;
}

本题运用的是双链表,顾名思义就是可以双向索引的链表,既可以向前索引,也可以向后索引。

1.纵览全局,我们需要三个数组,数组e存放输入的数值,数组l存放左指针,数组r存放右指针

简言之就是 A-->B-->C(右指针),A<--B<--C(左指针)。

2.初始化阶段,初始位置只有右指针,没有左指针。让idx=2,这样会导致第k个插入的数的索引值为k+1。

3.add函数实现向第k个数之后添加x,向数组e中存入x,将x的右指针赋值为k的右指针,将x的左指针指向k,将第k位数向右指向的数的左指针指向x,将第k位数的右指针指向x,最后让idx自增。

结合论述举例在B之后插入D

A-->B-->C            A<--B<--C 

A-->B-->D-->C     A<--B<--D<--C 

4.remove函数实现的是删除数据,实际上本质思想和上面的忽略跳过是一样的。

将第k个数删除,那么帝k个数指向的左边的数的指向向右的数应该是第k个数指向向右的数,另一边也是同理可知。

主体函数的实现也是比较简单的。需要注意的是最后的for循环中i!=1指的是右指针的边界点

  • 14
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值