在往常的单向链表中,调试往往是一件非常痛苦的事情。因此,我们选用数组代替链表进行调试。
这时候我们需要两个数组:
left[i],right[i]
其中,left数组表示i左侧的数,right数组表示i右面的数。
如果我们想要从左向右遍历,可以用for语句
for(int i = head ; i ; i = right[i] )
后来在图论中,邻接链表也可以用【链式前向星】数组表示,这里暂且不提。
以此题为例。
一个学校里老师要将班上N个同学排成一列,同学被编号为1∼N,他采取如下的方法:先将1号同学安排进队列,这时队列中只有他一个人;2−N号同学依次入列,编号为i的同学入列方式为:老师指定编号为i的同学站在编号为1∼(i−1)中某位同学(即之前已经入列的同学)的左边或右边;从队列中去掉M(M<N)个同学,其他同学位置顺序不变。在所有同学按照上述方法队列排列完毕后,老师想知道从左到右所有同学的编号。输入格式第1行为一个正整数N,表示了有N个同学。第2−N行,第i行包含两个整数k,p。其中k为小于i的正整数,p为0或者1。若p为0,则表示将i号同学插入到k号同学的左边,p为1则表示插入到右边。第N+1行为一个正整数M,表示去掉的同学数目。接下来M行,每行一个正整数x,表示将x号同学从队列中移去,如果x号同学已经不在队列中则忽略这一条指令。输出格式1行,包含最多N个空格隔开的正整数,表示了队列从左到右所有同学的编号,行末换行且无空格。
。。。
显然此题想让我们进行两个操作:插入元素,删除元素
对于插入元素
if(p == 0){//i为要插入的元素
int y = l[k];
r[y] = i;
l[i] = y;
r[i] = k;
l[k] = i;
}
if(p == 1){
int x = r[k];
l[x] = i;
l[i] = k;
r[i] = x;
r[k] = i;
}
对于删除元素,注意删除的元素t ,left【t】,right【t】都要改为0;这样,才能相当于free(t)这一操作。
if(l[t]==0&&r[t]==0) continue;
if(l[t] ==0){
l[r[t]] = 0;
continue;
}
if(r[t]==0){
r[l[t]] = 0;
}
int le = l[t];
int ri = r[t];
r[le] = ri;
l[ri] = le;
l[t] = 0;//删除时非常重要,笔者第一次在这里WA一发
r[t] = 0;
最后,遍历时我们需要找出head头节点:
for(int i=1;;i++)
{
if(l[i]!=0){
head = i;
break;
}
}
while(l[head]) head = l[head];
附上原代码:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<cmath>
#include<queue>
#include<stack>
#include<algorithm>
#define ll long long
using namespace std;
int r[200010];
int l[200010];
int main()
{
int n;
cin>>n;
l[1] = 0;r[0] = 1;r[1] = 0;l[0] = 0;
int head;
for(int i=2;i<=n;i++)
{
int k,p;
cin>>k>>p;
if(p == 0){
int y = l[k];
r[y] = i;
l[i] = y;
r[i] = k;
l[k] = i;
}
if(p == 1){
int x = r[k];
l[x] = i;
l[i] = k;
r[i] = x;
r[k] = i;
}
}
int m;
cin>>m;
for(int i=1;i<=m;i++)
{
int t;
cin>>t;
if(l[t]==0&&r[t]==0) continue;
if(l[t] ==0){
l[r[t]] = 0;
continue;
}
if(r[t]==0){
r[l[t]] = 0;
}
int le = l[t];
int ri = r[t];
r[le] = ri;
l[ri] = le;
l[t] = 0;//删除时非常重要
r[t] = 0;
}
for(int i=1;;i++)
{
if(l[i]!=0){
head = i;
break;
}
}
while(l[head]) head = l[head];
for(int i=head;i;i = r[i]){
cout<<i<<" ";
}
}