[蓝桥杯][2019年第十届真题]---修改数组---并查集

题目描述:

给定一个长度为 N 的数组 A = [A1, A2, · · · AN ],数组中有可能有重复出现 的整数。
现在小明要按以下方法将其修改为没有重复整数的数组。小明会依次修改 A2,A3,··· ,AN。
当修改 Ai 时,小明会检查 Ai 是否在 A1 ∼ Ai−1 中出现过。如果出现过,则 小明会给 Ai 加上 1 ;如果新的 Ai 仍在之前出现过,小明会持续给 Ai 加 1 ,直 到 Ai 没有在 A1 ∼ Ai−1 中出现过。
当 AN 也经过上述修改之后,显然 A 数组中就没有重复的整数了。 现在给定初始的 A 数组,请你计算出最终的 A 数组

输入:

第一行包含一个整数 N。 第二行包含N个整数A1,A2,··· ,AN

输出:

输出N个整数,依次是最终的A1,A2,··· ,AN。

思路:

如果暴力解的话,肯定是会超时的,比如1-10000中的数都已经存在了,如果我再输入一个1,那么他会遍历10000次,每次都是这样,时间复杂度相当高,那么我现在应该考虑的是,如何跳过这10000个数字,直接将1变为10001,这里用并查集来解决这个问题,关于并查集,这里有详细介绍。

这道题对于并查集的使用,查找操作还不变,就是寻找该节点的根节点,合并操作稍微变了一下,用数组f[i]表示i的根节点为f[i],初始时初始化为f[i]=i,根节点就是自己本身。例如,输入第一个数字为2,此时2已经出现过了,那么下次再出现2的时候,我就应该把2变为3,因此我就将f[2]的值赋为3,合并操作就是让输入这个数字的根节点和根节点+1,进行合并,取大的作为根节点。


比如输入5个数字分别为2,1,1,3,4。下边模拟一遍这个过程:

  1. for(int i=0;i初始化操作
  2. 第一个输入2,2没出现过,最终结果就是2,此时为了防止以后再出现2,将2的根节点赋为3。
  3. 输入1,1没有出现过,最终结果就是1,将1的根节点赋为2。
  4. 输入1,1的根节点为2,在寻找2的根节点,为3,这里就把1修改为3,然后将3的根节点赋为4。
  5. 输入3,3的根节点为4,将4的根节点赋为5.
  6. 输入4,4的根节点为5,将5的根节点赋为6
  7. 输出2,1,3,4,5

代码:

#include
using namespace std;

const int maxx=100005;
int f[maxx],ans[maxx];//f[i]表示i的根节点为f[i] 
int n;

int findx(int x)//寻找x的根节点 
{
	int r=x;
	while(f[r]!=r)
		r=f[r];
	return r;
}
void merge(int a,int b)//合并集合a和集合b 
{
	int t1=findx(a);
	int t2=findx(b);
	if(t1>t2) f[t2]=t1;
	else if(t1<t2) f[t1]=t2;
}
int main()
{
	cin>>n;
	for(int i=0;i<maxx;i++) 
		f[i]=i;
	int x;
	for(int i=1;i<=n;i++)
	{
		cin>>x;
		ans[i]=findx(x);
		merge(ans[i],ans[i]+1);
	}

	for(int i=1;i<=n;i++) 
		cout<<ans[i]<<" ";
	cout<<endl;
	return 0;
}

你可能感兴趣的:(算法,数据结构)