luogu1160:队列安排:双向链表/树的中序遍历

题目连接

  • 该题是luogu试炼场的2-13:T4

题目大意

  1. n个数字组成的队列,多次的插入;
  2. 再删除其中m个元素;
  3. 要求输出最后的队列状态

题目分析

  • 看题目第一反应是队列:

  • 但因为多次的间隔插入和条空删除,应该要用链表来做,链表的思想请参考

  • 思路1:双向链表

  • 思路2:用树来存储,中序遍历输出


解题思路1:双向链表

  1. 最开始的时候队伍里只有1号同学;
  2. 接下来的n-1个同学,插入到k同学的左边或者右边;
  3. 所以每次 i 插入的时候,更新 i 左右的元素的链接。
  4. 需要记录队头,因为最后要输出。

代码:

只要搞清楚前驱和后继就好

//luogu1160:队列安排 
//双向链表 
 
#include
using namespace std;
 
int n,m,x,k,p;
int f[100005],s[100005];
  
int main()
{
	scanf("%d",&n);
	memset(f,0,sizeof(f));
	memset(s,0,sizeof(s));
	
	int t=1;//队头 
	for(int i=2;i<=n;i++)//i号同学进队 
	{
		scanf("%d %d",&k,&p);
		if(p==0)//k站在 i 的左边 
		{
			f[i]=f[k]; s[i]=k;
			s[f[k]]=i; f[k]=i;
			if(k==t) t=i;//更新队头 
		}
		if(p==1)//i站在 k 的左边
		{
			f[i]=k; s[i]=s[k];
			f[s[k]]=i; s[k]=i;
		}
		
		
	}

	scanf("%d",&m);
	while(m--)
	{
		scanf("%d",&x);
		if(f[x]==0&&s[x]==0) continue;//x已经不在队列中
		
		if(t==x) t=s[x];//更新队头 
		
		s[f[x]]=s[x];//父亲的儿子更新 
		f[s[x]]=f[x];//儿子的父亲更新 
		s[x]=f[x]=0;//x出队 
	}
	
	//输出 
	for(int j=t;s[j]!=t;j=s[j])
	{
		printf("%d ",j);
	}

	return 0;
}




思路2:用树来存储,中序遍历输出

  • 将前驱放在左儿子;
  • 将后继放在有儿子;
  • 用 v 来纪录当前点是否被删除;
  • 中序便利输出答案。

代码2:

  • 结构体的使用,树的存储,中序遍历
//luogu1160:队列安排 
//树的存储与中序遍历 
 
#include
using namespace std;
 
int n,m,k,p;
struct nod{int s1,s2,v;nod(){ s1=s2=v=0;} }a[100005];
//s1表示前驱,s2表示后继,v表示是否在队列内 

void dfs(int x)//中序:按 左->中->右 的顺序遍历整棵树 
{
	if(!x) return ;//空点:边界 
	
	dfs(a[x].s1);//搜前驱(左儿子) 
	
	if(a[x].v==0) printf("%d ",x);//如果自己不为空,输出
	
	dfs(a[x].s2);//搜后继(右儿子) 
	
}

int main()
{
	scanf("%d",&n);
	
	//建立树 
	for(int i=2;i<=n;i++)
	{
		scanf("%d %d",&k,&p);
		
		if(p==0)
		{
			if(a[k].s1)//k有前驱,更改 
			{
				a[i].s1=a[k].s1; 
				a[k].s1=i;
			}
			else a[k].s1=i;//k没有前驱,直接赋值 
		}
		if(p==1)
		{
			if(a[k].s2)
			{
				a[i].s2=a[k].s2;
				a[k].s2=i;
			}
			else a[k].s2=i;
		}
	}
	scanf("%d",&m);
	while(m--)
	{
		scanf("%d",&k); a[k].v=1;//删除 k 点 
	} 
	
	dfs(1);//中序遍历,输出 
	
	return 0;
}



你可能感兴趣的:(题解,深搜,luogu,递归,大礼包,luogu1160,队列安排,双向链表,中序遍历)