1722-并查集求执行交换操作后的最小汉明距离

并查集操作一般步骤:

  • 并查集模版套上

  • 构建映射关系<root节点,集合> 

  • 执行相关操作,得到题目结果

看到题目后,首先我的做法是:

  • 将allowedSwaps分成可以互相交换的集合,比如[[0,1],[2,3]],分成两个集合[0,1]和[2,3]
  • 对每个集合对应的下表排序, 因为一个集合中的下标是可以随便排列的,souce=[1,2,3,4]每个集合子集排序后是[1,2,3,4],target每个子集排序后是[1,2,4,5]
  • 然后比较每个位置不同数字个数,为1

其中第一步,我使用了递归,超时.....,又使用了广度遍历,超时......,其实我也发现了这个问题,就是存在大量的重复计算,但是却又无能为力😭

最后,看了题解,发现是使用并查集...........

 

题解:


class Solution {

    private class UnionFind{
        // 节点的祖先节点
        private int []parent;
        // 节点的高度,开始时随机选一个做父亲,父亲的高度越来越高,就变成祖先了
        private int []rank;

        // 初始化,自己是自己的祖先
        public UnionFind(int n){
            this.parent = new int[n];
            this.rank = new int[n];
            for(int i=0; i<n; i++){
                this.parent[i] = i;
                this.rank[i] = 1;
            }
        }

        // 高度高的是祖先,合并
        public void union(int x, int y){
            int rootX = find(x);
            int rootY = find(y);
            if(rootX == rootY){
                return;
            }
            if(rank[rootX] == rank[rootY]){
                parent[rootX] = rootY;
                rank[rootY]++;
            }else if(rank[rootX] < rank[rootY]){
                parent[rootX] = rootY;
            }else {
                parent[rootY] = rootX;
            }
        }

        // 找祖先
        public int find(int x){
            if(x != parent[x]){
                parent[x] = find(parent[x]);
            }
            return parent[x];
        }
    }

    public int minimumHammingDistance(int[] source, int[] target, int[][] allowedSwaps) {

        int res = 0;
        // 如果不交换
        if(allowedSwaps.length == 0){
            for(int i=0; i<source.length; i++){
                if(source[i] != target[i]){
                    res++;
                }
            }
            return res;
        }

        // 第一步,将任意交换结点对输入并查集
        int len = source.length;
        UnionFind unionFind = new UnionFind(len);

        for(int allowedSwap[]: allowedSwaps){
            int index1 = allowedSwap[0];
            int index2 = allowedSwap[1];
            unionFind.union(index1, index2);
        }

        // 第二步,构建映射关系 <祖先节点,<可交换的值,出现次数>>
        Map<Integer, Map<Integer, Integer>> hashMap = new HashMap<>(len);
        for(int i=0; i<len; i++){
            // 节点的祖先
            int root = unionFind.find(i);
            // 同一个祖先的节点,对应同一个集合
            if(hashMap.containsKey(root)){
                Map<Integer, Integer> map = hashMap.get(root);
                if(map.containsKey(source[i])) {
                    map.put(source[i], map.get(source[i]) + 1);
                }else {
                    map.put(source[i], 1);
                }
            }else {
                hashMap.computeIfAbsent(root, key -> new HashMap<>()).put(source[i], 1);
            }
        }

        // 第三步,执行详细操作
        for(int i=0; i<source.length; i++){
            // 找到祖先节点
            int root = unionFind.find(i);
            // 找到祖先对应的集合,也就是当前可交换的元素及出现次数
            Map<Integer, Integer> map = hashMap.get(root);
            // 如果存在可交换的,则执行map更新
            if(map.containsKey(target[i])) {
                int nums = map.get(target[i]);
                if(nums == 1) {
                    map.remove(target[i]);
                }else {
                    map.put(target[i], nums-1);
                }
                // 如果不存在,则res++
            }else {
                res++;
            }
        }
        return res;
    }
}

第二步:

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值