题解:使用STL中的list
迭代器的类型是list<Tp>::iterator(这里模板参数Tp需要与你操作的链表一致)。
迭代器的用法和指针有些像,可以用*运算符访问内部的元素,++和--运算符可以将它后移或前移一位(建议写成前置形式),用==和!=运算符进判断两个迭代器所指的位置是否一致。但要注意:list的迭代器不支持it += x或it1 - it2这样的运算,也不支持<,<=等运算符。
begin()成员函数返回指向头部元素的迭代器。
end()成员函数返回指向末尾位置的迭代器。这个“末尾位置”指的是最后一个元素再往后一位,也就是说end()所指的位置不包含有效元素,它相当于一个虚设的节点。这样设计是为了满足C++标准库表示区间时左闭右开的惯例。
insert(it, val)成员函数用于在链表中插入元素。it为该链表的一个迭代器,val为待插入的值,插入后val位于it所指位置的前一位。返回值为一个迭代器,表示val插入到了哪个位置。
remove(it)成员函数用于删除某个迭代器所指的节点。注意在删除之后it就失效了,除非给it重新赋值,否则对它的任何操作都会导致错误!
除上述主要操作以外,list还提供了其他一些实用的成员函数:size()返回链表内元素的个数,empty()判断链表是否为空,remove(val)用于移除所有值为val的节点,以及作为成员函数的sort()和unique()。(注意sort(myList.begin(), myList.end())是错误的写法)
附上代码:
#include<bits/stdc++.h>
using namespace std;
using Iter=list<int>::iterator;
const int maxn=1e5+10;
Iter pos[maxn];
list<int>quelist;
bool erased[maxn];
int n;
void buildqueue()
{
quelist.push_front(1);
pos[1]=quelist.begin();
for(int i=2;i<=n;i++){
int k,p;
scanf("%d%d",&k,&p);
if(p==0){
pos[i]=quelist.insert(pos[k],i);
}else{
auto nextIter=next(pos[k]);
pos[i]=quelist.insert(nextIter,i);
}
}
int m;
scanf("%d",&m);
int x;
for(int i=1;i<=m;i++){
scanf("%d",&x);
if(!erased[x]){
quelist.erase(pos[x]);
}
erased[x]=true;
}
}
int main()
{
scanf("%d",&n);
buildqueue();
bool first=true;
for(list<int>::iterator iter=quelist.begin();iter!=quelist.end();iter++){
if(!first){
printf(" ");
}
first=false;
printf("%d",*iter);
}
printf("\n");
return 0;
}
题解二:模拟链表
题解:使用数组进行模拟链表,和邻接表非常像
#include<cstdio>
#include<cstring>
int a[100010][3],n,m;
//a[i][2]表示学号为i的同学右边同学的学号
//a[i][3]表示学号为i的同学左边同学的学号
int main()
{
scanf("%d",&n);
int j=1;
memset(a,0,sizeof(a));
a[1][1]=1;
for(int i=2;i<=n;i++)
{
int x,y;
scanf("%d %d",&x,&y);
a[i][1]=i;
if(y==0)
//插入左边
{
a[a[x][3]][2]=i;
a[i][2]=x;
a[i][3]=a[x][3];
a[x][3]=i;
if(x==j) j=i;//最左端
//比较麻烦,要改链表
}
else
//插入右边
{
a[i][2]=a[x][2];
a[a[x][2]][3]=i;
a[x][2]=i;
a[i][3]=x;
}
}
scanf("%d",&m);
for(int i=1;i<=m;i++)
{
int x;
scanf("%d",&x);
if(a[x][1]!=0)
//该同学还在
{
a[x][1]=0;
//踢掉……(好可怜)
a[a[x][3]][2]=a[x][2];
a[a[x][2]][3]=a[x][3];
n--;
if(x==j) j=a[x][2];
}
}
int i=1,x=j;
while(i<=n)
{
printf("%d ",a[x][1]);
x=a[x][2]; i++;
}
return 0;
}
题解三:使用底层实现的双向链表
需要做的一件事,就是设置好已经在队列中的1号同学的数据信息,由于在程序刚刚开始运行时,队列中只有1号同学一个人,为了方便后面的*插入*和*删除*的操作,因此,1号同学的前面和后面都是1号同学
que[1].back = &que[1]; que[1].front = &que[1];
附上代码:
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
struct queueDat{
int ID; //该同学的号码
queueDat *front = NULL, *back = NULL;
}que[100001];
queueDat *head = &que[1];
void _cut(int ID)
{
queueDat *cut = &que[ID];
if (cut->ID == head->ID){
head = cut->back;
}
cut = cut->front;
cut->back = cut->back->back;
cut = cut->back;
cut->front = cut->front->front;
}
void _add(int num, int ID, bool back)
{
queueDat *find = &que[ID], *add = &que[num];
if (back){ //插入在后面
add->front = find;
add->back = find->back;
find->back = add;
find = find->back->back;
find->front = add;
return;
}else{ //插入在前面
add->back = find;
add->front = find->front;
find = find->front;
find->back = add;
find = find->back->back;
find->front = add;
if (ID == head->ID){
head = add;
}
}
}
int main()
{
bool inQueue[100001];
memset(inQueue, false, sizeof(inQueue));
inQueue[1] = true;
for (int i = 1; i < 100001; i++){
que[i].ID = i; //que[i]对应i号同学
}
que[1].back = &que[1];
que[1].front = &que[1];
int totStudents, a, b;
cin >> totStudents; //同学总数
for (int i = 2; i <= totStudents; i++){ //i代表同学的号码
inQueue[i] = true; //确认i号同学在队列当中
cin >> a >> b; //插入在a同学的 左边或右边
_add(i, a, ((b == 0) ? false : true));
}
cin >> totStudents;
for (int i = 0; i < totStudents; i++){
cin >> a;
if (inQueue[a] == true){
inQueue[a] = false;
_cut(a);
}
}
b = head->ID;
do{
cout << head->ID << " ";
head = head->back;
} while (b != head->ID);
cout << endl;
return 0;
}
题解四:使用树,太机智了
附上代码:
#include<iostream>
#include<cstdio>
using namespace std;
const int maxn=101000;
struct node{
int lc,rc,v;
};
node d[maxn];
void dfs(int x)
{
if(x==-1){
return ;
}
dfs(d[x].lc);
if(d[x].v==0){
printf("%d ",x);
}
dfs(d[x].rc);
}
int main()
{
int n,m;
scanf("%d",&n);
d[1].v=0;
d[1].lc=d[1].rc=-1;
for(int i=2;i<=n;i++){
int x,y;
scanf("%d%d",&x,&y);
d[i].lc=d[i].rc=-1;
d[i].v=0;
if(y==0){
if(d[x].lc!=0){
d[i].lc=d[x].lc;
d[x].lc=i;
}else{
d[x].lc=i;
}
}else{
if(d[x].rc!=0){
d[i].rc=d[x].rc;
d[x].rc=i;
}else{
d[x].rc=i;
}
}
}
scanf("%d",&m);
for(int i=1;i<=m;i++){
int x;
scanf("%d",&x);
d[x].v=1;
}
dfs(1);
return 0;
}
题解五:使用splay
先加入两个哨兵结点0和n+1
插入操作 将编号为k的同学旋转到根
若p==1,编号k的下一位同学旋转到根的右子树, 然后将带插入的结点直接插入根的右子树的左子树
若p==0,编号k的上一位同学旋转到根的左子树, 然后将带插入的结点直接插入根的左子树的右子树
删除操作
找到编号x的同学的上一个同学旋转到根, 下一个同学旋转到根的右子树, 然后直接删除根的右子树的左子树
对于重复的开一个judge数组记录就好了
最后中序遍历输出
#include<iostream>
#include<cstdio>
#include<vector>
#include<queue>
#include<algorithm>
#include<cstring>
using namespace std;
int read()
{
int f=1,x=0;
char ss=getchar();
while(ss<'0'||ss>'9'){if(ss=='-')f=-1;ss=getchar();}
while(ss>='0'&&ss<='9'){x=x*10+ss-'0';ss=getchar();}
return x*f;
}
const int maxn=200010;
int n,m;
int ch[maxn][2],fa[maxn],rt=1;
int size[maxn],v[maxn],pos[maxn],sz;
int judge[maxn];
void print(int p)
{
if(!p) return;
print(ch[p][0]);
if(v[p]!=0&&v[p]!=n+1)printf("%d ",v[p]);
print(ch[p][1]);
}
void update(int p)
{
size[p]=size[ch[p][0]]+size[ch[p][1]]+1;
}
void rotate(int& p,int x)
{
int y=fa[x],z=fa[y];
int d=(ch[y][0]==x);
if(y==p) p=x;
else if(ch[z][0]==y) ch[z][0]=x;
else ch[z][1]=x;
fa[y]=x; fa[ch[x][d]]=y; fa[x]=z;
ch[y][d^1]=ch[x][d]; ch[x][d]=y;
update(y); update(x);
}
void splay(int& p,int x)
{
while(x!=p)
{
int y=fa[x],z=fa[y];
if(y!=p)
{
if((ch[y][0]==x)^(ch[z][0]==y)) rotate(p,x);
else rotate(p,y);
}
rotate(p,x);
}
}
void insert(int x)
{
v[++sz]=x; pos[x]=sz;
size[sz]=1; ch[sz][1]=ch[sz][0]=0;
if(sz>1)
{
ch[sz-1][1]=sz; fa[sz]=sz-1;
splay(rt,sz);
}
}
int find(int p,int k)
{
int ss=size[ch[p][0]];
if(k==ss+1) return p;
if(k<=ss) return find(ch[p][0],k);
else return find(ch[p][1],k-ss-1);
}
void ins(int x,int d,int k)//k号同学在x号同学的右(p==1)/左(p==0)边
{
splay(rt,x);int y;
if(d==1) y=find(rt,size[ch[x][0]]+2); //插入右边,找到x的下一个
else y=find(rt,size[ch[x][0]]);//插入左边,找到x的上一个
splay(ch[x][d],y);
v[++sz]=k; pos[k]=sz;//插入
size[sz]=1; ch[sz][1]=ch[sz][0]=0;
fa[sz]=y; ch[y][d^1]=sz;
update(y); update(x);
}
void del(int x)
{
splay(rt,x);
int y=find(rt,size[ch[x][0]]); //x的上一个同学
int z=find(rt,size[ch[x][0]]+2); //x的下一个同学
splay(rt,y); splay(ch[y][1],z);//上一个伸展到根,下一个伸展到根的右子树
fa[ch[z][0]]=0; ch[z][0]=0;//删除
update(z); update(y);
}
int main()
{
n=read();
insert(0); insert(n+1);//哨兵结点
ins(pos[0],1,1);//先让1号入队
for(int i=2;i<=n;++i)
{
int k=read(),p=read();
ins(pos[k],p,i);
}
m=read();
while(m--)
{
int x=read(); if(judge[x]) continue;
judge[x]=1;
del(pos[x]);
}
print(rt);
return 0;
//niiick
}