题意
一个学校里老师要将班上N个同学排成一列,同学被编号为1∼N,他采取如下的方法:
先将1号同学安排进队列,这时队列中只有他一个人;
2−N号同学依次入列,编号为i的同学入列方式为:老师指定编号为i的同学站在编号为1∼(i−1)中某位同学(即之前已经入列的同学)的左边或右边;
从队列中去掉编号为M(M<N)个同学,其他同学位置顺序不变。
在所有同学按照上述方法队列排列完毕后,老师想知道从左到右所有同学的编号
输出 输出最后队列从左至右所有同学的编号
思路 这道题需要较快的删除、插入。
大部分小伙伴可能跟我的想法一样,第一时间想到的便是链表,所以我们就开始按照之前的知识,通过指针这一结构性开销,设计我们的链表,然后测试案例也能过,结果交上去😢
没错,超时了 ,这时可能大家才注意到,这个题的核心除了能较快的插入和删除意外,还需要较快的查询。
我相信之前看过我博客的小伙伴们,是知道我最近在打紫书,那么了解紫书的小伙伴们,应该知道紫书还介绍了一种设计链表的方法——用数组模拟。
用数组模拟,(以双向链表为例),用两个数组next,pre分别代替原来的*next 和 *pre,其实与使用指针的模拟差不多,只不过把指针换作了数组,但是用数组模拟,在这种情况下,查找效率更高
知道思路了,让我们回顾一下链表的插入和删除
链表的插入和删除 (这里是使用数组模拟的,记得回想用指针模拟时的操作)
插入
假如 i 是需要插入位置的上一个位置 k为需要插入的元素
指针: 数组
k->next=i->next; next[k]=next[i];
i->next->pre=k; pre[next[i]]=k;
i->next=k; next[i]=k;
k-->pre=i; pre[k]=i;
删除
假如i 是需要删除位置
指针: 数组:
i->pre->next=i->next; next[pre[i]]=next[i];
i->next->pre=i->pre; pre[next[i]]=pre[i];
代码实现:
#include<iostream>
#include<cstring>
using namespace std;
const int N=1e5+5;//
int Next[N],pre[N],nums[N];//开辟数组,nums是判断这个数还在不在链表里
void print();
int main()
{
int n;
scanf("%d",&n);
memset(nums,0,sizeof(nums));
Next[0]=1;//做个标志,没有任意个数的next为1,因为数是从1开始的
pre[0]=n;//做个标记,没有任意个数的pre为n,因为数是从n结束的
Next[1]=0;
pre[1]=0;
int k,p;
for(int i=2;i<=n;i++)
{
scanf("%d%d",&k,&p);
//插入操作
if(p==0)
{
Next[i]=k;
pre[i]=pre[k];
Next[pre[i]]=i;
pre[k]=i;
}
else
{
Next[i]=Next[k];
pre[Next[k]]=i;
Next[k]=i;
pre[i]=k;
}
}
int M,x;
scanf("%d",&M);
//删除操作
while(M--)
{
scanf("%d",&x);
if(nums[x]==0)
{
Next[pre[x]]=Next[x];
pre[Next[x]]=pre[x];
nums[x]=1;
}
}
//打印答案
print();
}
void print()
{
int i=Next[0];
while(i!=0)//如果i的下标为0,则就不输出了
{
printf("%d ",i);
i=Next[i];
}
printf("\n");
}
个人总结
做一件事最需要的就是持之以恒的精神,虽然目前为止我只写了五篇题解,但是我会继续坚持下去的