并查集--使数组唯一的最小增量945

可以让数组中的某个数加1,求加几次之后数组中所有的数都是唯一的。
先将数组排序,现在遍历数组,如果当前数大于等于前一个数加1,则说明不用处理,令前一个数等于当前数,如果小于前一个数加1,则要把它变到前一个数加1,并把前一个数变成前一个数加1.

class Solution {
    public int minIncrementForUnique(int[] A) {
        if(A==null || A.length<2)return 0;
        Arrays.sort(A);
        int res=0;
        int preNum=A[0];
        for(int i=1;i<A.length;i++){
            if(A[i]>=preNum+1){
                preNum=A[i];
            }else{
                res+=preNum+1-A[i];
                preNum++;
            }
        }
        return res;
    }
}

并查集

遍历元素,将这个数组放到连通分量中,如果发现这个元素不能放到指定位置,就要顺次找到这个连通分量的最大值,然后将这个元素放在下一个,然后将它加入连通分量。
并查集就有两个操作,第一个是合并(union),第二个是查询find这个元素的连通分量的根节点(find)。(两个元素都查根节点可以找到他们是不是在一个连通分量中)。
初始化一般就是有多少个元素制造多少个连通分量,然后进行别的操作。可以用数组或者是hashmap表示。一个元素为x,则它的上一个元素为f(x),在数组中也就意味着值等于下标的值就是根节点。
find也就是找到根节点,如果x等于f(x),意味着这就是根节点,否则返回find(f(x))。可以进行的优化就是将一个连通分量中所有的节点都连在根节点上。这样的话find操作就是:如果x等于f(x),则返回x,如果不等于,则将f(x)的父节点设置为f(x),并返回f(x)。
union就是找到两个集合的根节点,然后让一个集合的根节点的上一个元素为另一个集合的根节点。f(f(x))=f(y)。即让x集合的根节点的上一个节点为y的根节点。
则这个题中我们将连通分量中最大的元素设为根节点。如果遍历到新元素时不在并查集中,就将它加入,并且把左边右边的都加入,连起来。(加入就是创造新的连通分量,需要和原来的连通分量进行合并)
如果在并查集中,说明这个数重复了,需要增加,增加到此时连通分量的根节点+1.记一下move的值。将增加过的数加入连通分量,(还是需要合并左和右)

class Solution {
    public int minIncrementForUnique(int[] A) {
         int len = A.length;
        if (len == 0) {
            return 0;
        }
        UnionFind unionFind = new UnionFind();
        int res = 0;
        for (int num : A) {
            if (unionFind.contains(num)) {//如果此时的值在并查集中,说明要自增
                int root = unionFind.find(num);
                int add = root + 1;
                res += (add - num);
                unionFind.init(add);
            } else {
                unionFind.init(num);//如果不在并查集中,说明不用自增。
            }
        }
        return res;
    }
    private class UnionFind {
    private int[] parent;//用数组实现
     public UnionFind() {//构造函数
        this.parent = new int[79999];
        // 应初始化成为 -1,表示这个元素还没放进并查集
        Arrays.fill(parent, -1);
      }
    public void init(int x) {//初始化
        parent[x] = x;//左右如果都在并查集则合并。
        if (x > 0 && parent[x - 1] != -1) {
            union(x, x - 1);
        }
        if (parent[x + 1] != -1) {
            union(x, x + 1);
        }
    }
    public boolean contains(int x) {//表示这个节点在不在并查集中
        return parent[x] != -1;
    }
    public int find(int x) {
        if(x==parent[x])return x;
        else{
           parent[x]=find(parent[x]);
           return parent[x];
        }
    }
    public void union(int x, int y) {
        int rootX = find(x);
        int rootY = find(y);
        // 注意:根据这个问题的特点
        // 只能把小的结点指向大的结点
        if (rootX < rootY) {
            parent[rootX] = rootY;
        }
        if (rootY < rootX) {
            parent[rootY] = rootX;
        }
    }
}
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值