Part 7.1 链表

题单

序言

在一个数列中高效插入或删除一个元素链表毫无疑问是最好的选择。

P1160 队列安排

思路

  1. 根据数据范围 1 < M ≤ N ≤ 1 0 5 1<M\leq N\leq10^5 1<MN105 和题目要求的高效插入和删除元素可知,需要用链表
  2. 关键是如何实现 O ( 1 ) \mathcal O(1) O(1) O ( log ⁡ n ) \mathcal O(\log n) O(logn) 地访问某个学生在链表中的存储下标。可以 f i d [ k ] fid[k] fid[k] 储存编号为 k k k 的学生在链表中的存储下标。当然若 k k k 过大,可以用 map 去存。具体的操作,需要注意地是,要特殊处理一下边界。
  3. 时间复杂度为 O ( n + m ) \mathcal O(n + m) O(n+m)

代码

#include <bits/stdc++.h>
using namespace std;
const int maxn = 1e5 + 100;
int cnt, n, m, vis[maxn], fid[maxn], head;
struct linklist
{
	int id, prev, next;
} node[maxn];
int main ()
{
	scanf ("%d", &n);
	node[++ cnt] = (linklist) {1, -1, -1};
	fid[1] = cnt, head = 1;
	for (int i = 2; i <= n; i++) 
	{
		int k, p;
		scanf ("%d %d", &k, &p);
		fid[i] = ++ cnt;
		if (p == 0) 
		{
			if (node[fid[k]].prev != -1)
				node[node[fid[k]].prev].next = cnt;
			node[cnt] = (linklist) {i, node[fid[k]].prev, fid[k]};
			node[fid[k]].prev = cnt;
		}
		else 
		{
			if (node[fid[k]].next != -1)
				node[node[fid[k]].next].prev = cnt;
			node[cnt] = (linklist) {i, fid[k], node[fid[k]].next};
			node[fid[k]].next = cnt;
		}
		if (node[cnt].prev == -1) head = cnt;
	}
	scanf ("%d", &m);
	for (int i = 1; i <= m; i++) 
	{
		int x;
		scanf ("%d", &x);
		if (vis[x]) continue; vis[x] = 1;
		if (node[fid[x]].prev == -1 && node[fid[x]].next != -1) 
			node[node[fid[x]].next].prev = -1, head = node[fid[x]].next;
		else if (node[fid[x]].next == -1 && node[fid[x]].prev != -1) 
			node[node[fid[x]].prev].next = -1;
		else if (node[fid[x]].prev != -1 && node[fid[x]].next != -1)
		{
			int previ = node[fid[x]].prev, nexti = node[fid[x]].next;
			node[previ].next = nexti, node[nexti].prev = previ; 
		}
		else return 0;
	}
	for (int i = head; i != -1; i = node[i].next) 
		printf ("%d ", node[i].id);
	return 0;
}

收获

  1. 知道了如何 O ( 1 ) \mathcal O(1) O(1) O ( log ⁡ n ) \mathcal O(\log n) O(logn) 地在链表中添加或删除一个元素,也就是离散化的应用。

P1996 约瑟夫问题

思路

  1. 是个环,于是需要将链表的首尾连接起来。然后按照题意,模拟删除即可。
  2. 时间复杂度为 O ( n m ) \mathcal O(nm) O(nm)

代码

#include <bits/stdc++.h>
using namespace std;
const int maxn = 110;
int n, m, cnt, p = 1, cur = 1;
struct linklisti 
{
	int id, prev, next;
} node[maxn];
int main ()
{
	scanf ("%d %d", &n, &m);
	for (int i = 2; i < n; i++)
		node[i] = (linklisti) {i, i - 1, i + 1};
	node[1] = (linklisti) {1, n, 2};
	node[n] = (linklisti) {n, n - 1, 1};
	while (n) 
	{
		if (p == m) 
		{
			printf ("%d ", node[cur].id);
			node[node[cur].prev].next = node[cur].next;
			node[node[cur].next].prev = node[cur].prev;
			p = 1, n --, cur = node[cur].next;
		}
		else p ++, cur = node[cur].next;
	}
	return 0;
}

收获

  1. 见识到了环形链表
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值