链表-题目讲解

题目:洛谷:P1160 队列安排。

大概思路

初始化

首先有一个数字 1 ,我们需要让接下来的数字站在它的左边或右边,这样就会改变它的链接关系,我们需要先建立一个链接关系,才能让它在后期时可以操作操作。因为 1 的前驱是 0 ,所以先让他和 0 连上,所以:next[0]=1,pre[1]=0;:将 0 的后继设为 1 , 1 的前驱设为 0 。最后要让 1 和他后面的数连上,这样做目的既可以有一个明确的结尾,也可以让后面输出的时候有停止的条件。总之:链接他的前后关系可以让它和其它之后的操作变得一样

所以:第一部分程序:

#include<bits/stdc++.h>//头文件 
using namespace std;//命名空间 
int next_[100001];//后继 
int pre_[100001];//前驱 
//因为next和pre已经在Dev-c++里有定义,所以加上下划线可以避免暴0 
int n;//n个人 
int m;//m次删除的操作 
int k;//放在k的左边或右边 
int p;//左边或右边 
int x;//删除的数 
int main(){
	cin>>n;//一共有n个人 
	next_[0]=1;//0的后继是1 
	pre_[1]=0;//1的前驱是0 
	next_[1]=-1;//建立末尾标记好理解 

输入与链接关系

因为 1 已经初始化了所以从 2 开始输入:

for(int i=2;i<=n;i++){
	cin>>k>>p;

p1 时,将 i 放在 k 的右边。

所以 k,i 的关系是 k->i

我们设想原来的关系是 k->x,而现在加入了 i ,从而将关系变成了 k->i->x。其中 x 是在 i 加入之前 k 的后继

所以 xi 加入之前为 next[k]

最后设定他们三之间的关系:

if(p==1){//k->x ==> k->i->x
	x=next_[k];
	next_[k]=i;//k->i
	next_[i]=x;//i->x
	pre_[i]=k;//k<-i
	pre_[x]=i;//i<-x
}

p0 时,将 i 放在 k 的左边。

所以 k,i 的关系是 i<-k

我们设想原来的关系是 x<-k,而现在加入了 i ,从而将关系变成了 x<-i<-k。其中 x 是在 i 加入之前 k 的前驱

所以 xi 加入之前为 pre[k]

最后设定他们三之间的关系:

if(p==0){//x<-k ==> x<-i<-k
	x=pre_[k];
	pre_[k]=i;//i<-k
	pre_[i]=x;//x<-i
	next_[x]=i;//x->i
	next_[i]=k;//i->k
}

所以:第二部分程序:

for(int i=2;i<=n;i++){//1已经给了所以从2开始放 
	cin>>k>>p;//在k的左或右 
	if(p==1){//k->x ==> k->i->x
		x=next_[k];//x在k的右边 
		next_[k]=i;//k->i
		next_[i]=x;//i->x
		pre_[i]=k;//k<-i
		pre_[x]=i;//i<-x
	}
	if(p==0){//x<-k ==> x<-i<-k
		x=pre_[k];//x在k的左边 
		pre_[k]=i;//i<-k
		pre_[i]=x;//x<-i
		next_[x]=i;//x->i
		next_[i]=k;//i->k
	}
}

删除部分

相比前两个部分,这个部分较为简单

首先有 m 个要删除的数,所以建立一个 for 循环:for(int i=1;i<=m;i++) 表示一共有 m 个要删除的数。

删除并不是真正意义上的删除,而是断开他的前后链接。

简单说成:

  • 将它前面的数和它后面的数连上,也就是将他的前驱的后继设为他的后继:next[pre[x]]=next[x];
  • 将它后面的数和它前面的数连上,也就是将他的后继的前驱设为他的前驱:pre[next[x]]=pre[x];

最后将此数彻底与数列断开:将他的前后设为 -1 或其他的题目没出现的数这样可以避免重复断开

程序:

cin>>m;//断开的次数 
for(int i=1;i<=m;i++){//断开m次 
	cin>>x;//要断开的数 
	if(pre_[x]==-1&&next_[x]==-1)continue;//已经断开过避免重复断开 
	next_[pre_[x]]=next_[x];//将前面和后面链接 
	pre_[next_[x]]=pre_[x];//将后面和前面链接 
	pre_[x]=-1;//断绝x的前面的连接 
	next_[x]=-1;//断绝x的后面的连接 
}

输出部分

第一个数是开头 0 (下标)的后继:next[0]

最后一个数是 -1 之前定义的,因为前面删除的数已经和前后彻底断开了链接,所以不用考虑 -1 串了,如果实在不放心可以将删除的 -1 改成 -2 或其他不可能出现的数

所以程序思路是:

从第一个数开始不停的链接后面的数并输出,直到到达 -1 (末尾)。

输出部分程序:

	int t=next_[0];//第一个数 
	while(t!=-1){//边界 
		cout<<t<<" ";//输出 
		t=next_[t];//连接的下一个数 
	}

总体程序

#include<bits/stdc++.h>//头文件 
using namespace std;//命名空间 
int next_[100001];//后继 
int pre_[100001];//前驱 
//因为next和pre已经在Dev-c++里有定义,所以加上下划线可以避免暴0 
int n;//n个人 
int m;//m次删除的操作 
int k;//放在k的左边或右边 
int p;//左边或右边 
int x;//删除的数 
int main(){
	cin>>n;//一共有n个人 
	next_[0]=1;//0的后继是1 
	pre_[1]=0;//1的前驱是0 
	next_[1]=-1;//建立末尾标记好理解 										//初始化
	
	
	for(int i=2;i<=n;i++){//1已经给了所以从2开始放 
		cin>>k>>p;//在k的左或右 
		if(p==1){//k->x ==> k->i->x
			x=next_[k];//x在k的右边 
			next_[k]=i;//k->i
			next_[i]=x;//i->x
			pre_[i]=k;//k<-i
			pre_[x]=i;//i<-x
		}
		if(p==0){//x<-k ==> x<-i<-k
			x=pre_[k];//x在k的左边 
			pre_[k]=i;//i<-k
			pre_[i]=x;//x<-i
			next_[x]=i;//x->i
			next_[i]=k;//i->k
		}
	}																		//输入与链接 
	
	
	cin>>m;//断开的次数 
	for(int i=1;i<=m;i++){//断开m次 
		cin>>x;//要断开的数 
		if(pre_[x]==-1&&next_[x]==-1)continue;//已经断开过避免重复断开 
		next_[pre_[x]]=next_[x];//将前面和后面链接 
		pre_[next_[x]]=pre_[x];//将后面和前面链接 
		pre_[x]=-1;//断绝x的前面的连接 
		next_[x]=-1;//断绝x的后面的连接 
	}																		//删除数字 
	
	 
	int t=next_[0];//第一个数 
	while(t!=-1){//边界 
		cout<<t<<" ";//输出 
		t=next_[t];//连接的下一个数 
	}																		//输出部分 
	
	
	return 0;//结束程序 
}

\color{red}感\color{gold}谢\color{orange}观\color{green}看\color{blue}!

2 个赞

我来讲另一个讲法,给我解决方案(

#include <bits/stdc++.h>
using namespace std;
int n,m;
int xx,p;
struct d{
    int left;
    int right;
}a[100010];
int yy;
int v[100010];
int main(){
    cin>>n;
    a[1].left=0;
    a[1].right=100010;
    a[0].right=1;
    for(int i=2;i<=n;i++){
        cin>>xx>>p;
        if(p==0)
        {
        a[i].left=a[xx].left,a[i].right=xx;
        a[a[xx].left].right=i;
        a[xx].left=i;
        }
        
    }
    bib=0;
    v[0]=true;
    for(int i=1;i<=n;i++){
        bib=a[bib].right;
        if(bib!=100010)cout<<bib<<" ";
        else break;
    }
    return 0;
} 

给了

2 个赞