提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档
目录
前言
链表是运用非常广泛的数据结构,除了查询可能要花点功夫外,增删都以O(n)的复杂度完成,学会用链表,是基础。本篇将以Luogu题单中(【数据结构1-1】线性表 )的一题为切入,总结模拟链表的心得。
学习永不停止,本博客将随着对链表的学习深入,不断更新。
一、题目
题目描述
一个学校里老师要将班上 NN 个同学排成一列,同学被编号为 1\sim N1∼N,他采取如下的方法:
先将 11 号同学安排进队列,这时队列中只有他一个人;
2-N2−N 号同学依次入列,编号为 ii 的同学入列方式为:老师指定编号为 ii 的同学站在编号为 1\sim(i-1)1∼(i−1) 中某位同学(即之前已经入列的同学)的左边或右边;
从队列中去掉 M(M<N)M(M<N) 个同学,其他同学位置顺序不变。
在所有同学按照上述方法队列排列完毕后,老师想知道从左到右所有同学的编号。
输入格式
第 11 行为一个正整数 NN,表示了有 NN 个同学。
第 2\sim N2∼N行,第 ii 行包含两个整数 k,pk,p,其中 kk 为小于 ii 的正整数,pp 为 00 或者 11。若 pp 为00,则表示将 ii 号同学插入到 kk 号同学的左边,pp 为 11 则表示插入到右边。
第 N+1N+1 行为一个正整数 MM,表示去掉的同学数目。
接下来 MM 行,每行一个正整数 xx,表示将 xx 号同学从队列中移去,如果 xx 号同学已经不在队列中则忽略这一条指令。
输出格式
11 行,包含最多 NN 个空格隔开的正整数,表示了队列从左到右所有同学的编号,行末换行且无空格。
说明/提示
样例解释:
将同学 22 插入至同学 11 左边,此时队列为:
2 1将同学 33 插入至同学 22 右边,此时队列为:
2 3 1将同学 44 插入至同学 11 左边,此时队列为:
2 3 4 1将同学 33 从队列中移出,此时队列为:
2 4 1同学 33 已经不在队列中,忽略最后一条指令
最终队列:
2 4 1数据范围
对于 20\%20% 的数据,有 1\leq N\leq 101≤N≤10;
对于 40\%40% 的数据,有 1\leq N\leq 10001≤N≤1000;
对于 100\%100% 的数据,有 1\leq N,M\leq1000001≤N,M≤100000。
//输入样例1 4 1 0 2 1 1 0 2 3 3//输出样例1 2 4 1
二、思路分析
1.“队列”emmm,要不试试队列模拟?
用队列模拟队列,考虑起来还是挺容易的。为了完成队列出入过程中对数据的删改,于是声明两个队列,模拟过程就在“一个出一个进”的操作中完成。
#include<iostream>
#include<queue>
using namespace std;
int n;
int k,p;
queue<int> q1,q2;
int main()
{
cin>>n;
q1.push(1);
for(int i=2;i<=n;++i)
{
cin>>k>>p;
if(!q1.empty() && q2.empty())
{
if(p==0)
{
while(!q1.empty())
{
if(q1.front()==k)
q2.push(i);
q2.push(q1.front());
q1.pop();
}
}
else
{
while(!q1.empty())
{
if(q1.front()==k)
{
q2.push(q1.front());
q2.push(i);
}
else
{
q2.push(q1.front());
}
q1.pop();
}
}
}
else
{
if(p==0)
{
while(!q2.empty())
{
if(q2.front()==k)
q1.push(i);
q1.push(q2.front());
q2.pop();
}
}
else
{
while(!q2.empty())
{
if(q2.front()==k)
{
q1.push(q2.front());
q1.push(i);
}
else
{
q1.push(q2.front());
}
q2.pop();
}
}
}
}
int m;
cin>>m;
for(int i=1;i<=m;++i)
{
cin>>k;
if(!q1.empty() && q2.empty())
{
while(!q1.empty())
{
if(q1.front()!=k)
{
q2.push(q1.front());
}
q1.pop();
}
}
if(!q2.empty() && q1.empty())
{
while(!q2.empty())
{
if(q2.front()!=k)
{
q1.push(q2.front());
}
q2.pop();
}
}
}
while(!q1.empty())
{
cout<<q1.front()<<" ";
q1.pop();
}
while(!q2.empty())
{
cout<<q2.front()<<" ";
q2.pop();
}
return 0;
}
喜提40分 ~~~~~TLE~~~~~
2.emmm插入/删除?这些个我链表兄弟熟啊!
①思路框架
总体就俩:插入/删除,直接上自定义函数封装这两个过程
输出:只要维护了队列排头,那么吸溜~~一长串都能遍历
小细节:
不在队列里的,emmm前一个后一个就设为INF吧~~ stu[i].last=INF; stu[i].next=INF;
在队列里,队头前一个还是队头:stu[i].last=i;
在队列里,队尾后一个还是队尾:stu[i].last=i;
②这里我用结构体模拟链表哦~
struct tp{
int last,next;
};
tp stu[100005]; //记录第i个同学前、后是谁,否则为INF
③add()函数封装
void add(int i,int k,bool p)
{
if(p)//插到右边
{
if(stu[k].next!=k)//如果非队尾
{
stu[i].next=stu[k].next;
stu[stu[k].next].last=i;
stu[k].next=i;
stu[i].last=k;
}
else
{
stu[k].next=i;
stu[i].last=k;
stu[i].next=i;
}
}
else
{
if(stu[k].last!=k)如果非队头
{
stu[i].last=stu[k].last;
stu[i].next=k;
stu[stu[k].last].next=i;
stu[k].last=i;
}
else
{
stu[k].last=i;
stu[i].next=k;
stu[i].last=i;
head=i;
}
}
}
④remove()函数封装
void remove(int k)
{
if(stu[k].last==INF && stu[k].next==INF)return;//不在队列里的话怎么能删除呢?
if(stu[k].next==k)//队尾
{
stu[stu[k].last].next=stu[k].last;
stu[k].last=INF;
stu[k].next=INF;
}
else if(stu[k].last==k)//队头
{
stu[stu[k].next].last=stu[k].next;
head=stu[k].last; //别忘了维护队列第一个同学是谁哦~~
stu[k].next=INF;
stu[k].last=INF;
}
else//普通队列内
{
stu[stu[k].last].next=stu[k].next;
stu[stu[k].next].last=stu[k].last;
stu[k].next=INF;
stu[k].last=INF;
}
}
三、完整代码
#include<iostream>
#include<cstdio>
#include<cmath>
#include<algorithm>
#include<cstring>
#include<iomanip>
#include<stack>
#define INF 0x3f3f3f3f
#define IOS ios::sync_with_stdio(0);cin.tie(0);cout.tie(0)
using namespace std;
struct tp{
int last,next;
};
tp stu[100005]; //记录第i个同学前、后是谁,否则为INF
void add(int,int,bool);
void remove(int);
int n,m;
int head;
int main()
{
IOS;
cin>>n;
int k;
bool p;
for(int i=1;i<=n;++i)
{
stu[i].last=INF;
stu[i].next=INF;
}//一开始无人在队列中
/************下面是入列部分************/
stu[1].last=1;
stu[1].next=1;
head=1;
for(int i=2;i<=n;++i)
{
cin>>k>>p;
add(i,k,p);//将i插入到k的p一侧
}
/*************下面是删除部分***********/
cin>>m;
for(int i=1;i<=m;++i)
{
cin>>k;
remove(k);
}
/*************下面是输出部分***********/
while(true)
{
cout<<head<<" ";
if(stu[head].next==head)break;
head=stu[head].next;
}
return 0;
}
void add(int i,int k,bool p)
{
if(p)//插到右边
{
if(stu[k].next!=k)
{
stu[i].next=stu[k].next;
stu[stu[k].next].last=i;
stu[k].next=i;
stu[i].last=k;
}
else
{
stu[k].next=i;
stu[i].last=k;
stu[i].next=i;
}
}
else
{
if(stu[k].last!=k)
{
stu[i].last=stu[k].last;
stu[i].next=k;
stu[stu[k].last].next=i;
stu[k].last=i;
}
else
{
stu[k].last=i;
stu[i].next=k;
stu[i].last=i;
head=i;
}
}
}
void remove(int k)
{
if(stu[k].last==INF && stu[k].next==INF)return;
if(stu[k].next==k)
{
stu[stu[k].last].next=stu[k].last;
stu[k].last=INF;
stu[k].next=INF;
}
else if(stu[k].last==k)
{
stu[stu[k].next].last=stu[k].next;
head=stu[k].last;
stu[k].next=INF;
stu[k].last=INF;
}
else
{
stu[stu[k].last].next=stu[k].next;
stu[stu[k].next].last=stu[k].last;
stu[k].next=INF;
stu[k].last=INF;
}
}
总结
最近开始刷数据结构——线性表的部分,加入小队以来开始有计划地学习,路阻且常(长)。本篇也是借机会把最基础的链表模拟给拿下。之后学习各种进阶链表,也会一直更新。
与诸君共勉!

本文通过解决Luogu数据结构题,讲解如何使用队列模拟来解决学校同学排队问题,涉及插入与删除操作,使用自定义链表结构实现。最终分享了完整代码和学习心得。

6万+

被折叠的 条评论
为什么被折叠?



