并查集练习1:蓝桥杯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

对于 80% 的评测用例,1 ≤ N ≤ 10000。

对于所有评测用例,1 ≤ N ≤ 100000,1 ≤ Ai ≤ 1000000。

输出

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

样例输入复制

5
2 1 1 3 4

样例输出复制

2 1 3 4 5

思路:

如果直接进行暴力遍历,会发现是时间复杂度是 1 + 2 + 3 ······ + n- 1 也就是O(n^2),对于数据量较大的用例来说很有可能超时,在这里我们使用并查集来解决

并查集是一棵树,对于每个元素来说,它既是一个数据也是一个位置。

在并查集中有两种操作,并和查。

1.并:

即合并,将两棵树合并为同一个

2.查:

即查找,在树中找到元素的根节点,进而判断二者是否在同一棵树中

我们利用一个数组prev[ ]来存储元素的父节点

在这一题中,我们首先将prev[]中每个元素的父都初始化为自己

接着,对于依次输入的每个数字,如果它是根节点,就输出它,同时pre[root]++; 如果不是根节点,我们就利用find()找到它的根节点,输出根节点, 然后pre[root]++

ps:相对于cin 和 cout ,printf 和 scanf的速度更快一些

#include
using namespace std;
const int MAX = 1000001;
int pre[MAX];
 
int find(int val) {
    if (pre[val] == val) return val;
    return pre[val] = find(pre[val]);
}

int main() {   
    for (int i = 0; i < MAX; i++) {
        pre[i] = i;
    }
    
    int nums, tmp;
    cin >> nums;
    for (int i = 0; i < nums; i++) {
        scanf("%d", &tmp);
        tmp = find(tmp);
        printf("%d ", tmp);
        pre[tmp] = tmp + 1;
    }
    
    return 0;
}

你可能感兴趣的:(蓝桥杯,算法,c++)