当前位置:   article > 正文

蓝桥杯-修改数组(并查集)_修改数组蓝桥杯

修改数组蓝桥杯

1、题目描述

  给定一个长度为 N 的数组 a = [ A 1 , A 2 , . . . , A N ] a=[A_1,A_2,...,A_N] a=[A1,A2,...,AN],数组中有可能有重复出现的整数。

  现在小明要按以下方法将其修改为没有重复整数的数组。小明会依次修改 A 2 , A 3 , . . . , A N A_2,A_3,...,A_N A2,A3,...,AN

  当修改 A i A_i Ai时,小明会检查 A i A_i Ai是否在中出 A 1 ∼ A i − 1 A_1\sim A_i-1 A1Ai1现过。如果出现过,则小明会给 A i A_i Ai加上 1 ;如果新的A_i仍在之前出现过,小明会持续给 加 A i A_i Ai 1 ,直 到 A i A_i Ai没有在 A 1 ∼ A i − 1 A_1\sim A_i-1 A1Ai1中出现过。

  当 A N A_N AN也经过上述修改之后,显然A数组中就没有重复的整数了。

  现在给定初始的 A 数组,请你计算出最终的 A 数组。

输入描述

  第一行包含一个整数 N

  第二行包含N个整数 A 1 , A 2 , . . . , A N A_1,A_2,...,A_N A1,A2,...,AN

  其中,1≤N 1 0 5 10^5 105,1≤* A i A_i Ai*≤ 1 0 6 10^6 106

输出描述

  输出 N 个整数,依次是最终的 A 1 , A 2 , . . . , A N A_1,A_2,...,A_N A1,A2,...,AN

  输入输出样例

示例

输入

5
2 1 1 3 4
  • 1
  • 2

输出

2 1 3 4 5
  • 1

运行限制

  • 最大运行时间:1s
  • 最大运行内存: 256M

2、解题思路

2.1 思路一:暴力法(超时)

  用一个exist[]数组标记输入的x是否已经被使用过exist[x]=1,若已经被使用过,则执行x++。这个思路倒是很清晰,就是超时了。

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.StreamTokenizer;
import java.util.ArrayList;


/**
 * 暴力:用一个数组exist标记输入的x是否被使用过(exist[x]=1),若已经被使用过,则执行x++
 */
public class Main {
    public static int[] exist=new int[1000010];
    public static StreamTokenizer st=new StreamTokenizer(new BufferedReader(new InputStreamReader(System.in)));
    public static void main(String[] args) throws IOException {
        int n = nextInt();
        ArrayList<Integer> list = new ArrayList<>();
        for (int i = 1; i <=n ; i++) {
            int x = nextInt();
            while(exist[x]==1){
                x++;
            }
            exist[x]=1;
            list.add(x);
        }
        list.forEach(x->{
            System.out.print(x+" ");
        });
    }
    public static int nextInt() throws IOException{
        st.nextToken();
        return (int)st.nval;
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33

2.2 思路二:并查集(AC)

  假设我们当前的输入为2 1 1 3 4,我们使用一个father[]数组保存每个节点的父节点,使用a数组保存最终每个位置上的结果。

  以输入[2,1,1,3,4]为例,初始的时候,我们让每个节点都指向自己。

//并查集初始化 
for (int i = 1; i <=MAXN; i++) {
      father[i]=i;
	}
  • 1
  • 2
  • 3
  • 4

  没输入一个数字,我们就去查找它的根节点,把他根节点的值当作这个位置的最终结果。

  比如,

  先输入2,查2的根节点为2,则最终结果:[2],然后将[2,3]合并,让2指向3,如下图:

image-20230331213222862

  这样做的目的是,在下一次还输入2的时候,直接把2的根节点值3当作本次的最终结果。

  再输入1,查1的根节点,1的根节点还是1,所以此时最终结果为:[2,1],然后将1和(1+1=2)执行合并,让1的根节点指向2的根节点,此时结果如下图:

image-20230331213536838

  接着输入1,查找1的根节点为3,直接把3当作本次的结果,则此时最终结果为:[2,1,3],然后执行3和(3+1=4)的合并,结果如下右图

image-20230331214820637

  接着输入3,查找3的根节点为4,则此时的最终结果为:[2,1,3,4],然后执行4和(4+1=5)的合并,结果如下图:

image-20230331214904881

  接着输入4,查找4的根节点为5,所以此时的最终结果:[2,1,3,4,5],然后执行5和(5+1=6)的合并就行,到这里已经结束了,就不再画出5和6的合并结果了。

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.StreamTokenizer;
import java.util.Arrays;
/**
 * 并查集
 */
public class Final {
    public static final int MAXN=1000000;
    public static int n;
    //并查集数组,father[i]
    public static int[] father=new int[1000010];
    public static int[] a=new int[1000010];//保存结果
    public static StreamTokenizer st=new StreamTokenizer(new BufferedReader(new InputStreamReader(System.in)));
    public static void main(String[] args) throws IOException {
        n=nextInt();
        init();
        for (int i = 1; i <=n; i++) {
            int x=nextInt();
            int root =find(x);
            a[i]=root;  //保存结果
            union(root,root+1);
        }
        Arrays.stream(a).skip(1).limit(n).forEach(x->{
            System.out.print(x+" ");
        });
    }
    //并查集初始化
    public static void init(){
        for (int i = 1; i <=MAXN; i++) {
            father[i]=i;
        }
    }
    //查找父节点:路径压缩,否则会超时
    public static int find(int x){
        if(father[x]==x){
            return x;
        }else{
            //注意在不断查找根节点的过程中要路径压缩
             father[x]=find(father[x]);
             return father[x];
        }
    }
    public static void union(int x,int y){
        int father_x = find(x); //找到x的祖先
        int father_y = find(y); //找到y的祖先
        father[father_x]=father_y;//让x的祖先指向y的祖先
    }
    public static int nextInt() throws IOException{
        st.nextToken();
        return (int)st.nval;
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54

image-20230331215052076

image-20230331215107412

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/酷酷是懒虫/article/detail/1013489
推荐阅读
相关标签
  

闽ICP备14008679号