LC上一道有趣的并查集题目

1722. 执行交换操作后的最小汉明距离

给你两个整数数组 source 和 target ,长度都是 n 。
还有一个数组 allowedSwaps ,
其中每个 allowedSwaps[i] = [ai, bi] 表示你可以交换数组 source 中下标为 ai 和 bi(下标从 0 开始)的两个元素。
注意,你可以按 *******任意 顺序 多次******* 交换一对特定下标指向的元素。

相同长度的两个数组 source 和 target 间的 *****汉明距离***** 是元素不同的下标数量。
形式上,其值等于满足 source[i] != target[i] (下标从 0 开始)的下标 i(0 <= i <= n-1)的数量。

在对数组 source 执行 任意 数量的交换操作后,
返回 source 和 target 间的 ******最小汉明距离****** 。
(链接:https://leetcode-cn.com/problems/minimize-hamming-distance-after-swap-operations)

这里解读一下题目:

1. 在allowedSwaps[i]里面, 只允许交换source数组中两个不同位置的值;
2. 交换的顺序和次数都是任意, 说明只要两个值可以交换, 这两个值就能交换, 并且交换后不影响其他值的相对位置
3. 汉明距离是指target和source中同一个索引位置, 但值不同的数量
4. 该题是求最小的汉明距离, 因此, 要利用allowedSwaps数组, 使tager和source中尽可能多的相同值处于相同的位置

解题思路:

1. 并查集:使用并查集, 将allowedSwaps数组中的索引位置***建立连通分量***,在同一个分量里面的任意两个索引位置可以***交换任意次***;
2. 使用mapSource<Integer, List>记录source数组中的每个值出现的索引位置;
3. 遍历数组target,
	(1)当target[i]和source[i]值相同时, 将mapSource中, source[i]对应的i位置删除;
	(2)当target[i]和source[i]值不相同时:
		a) 当source数组中含有target[i]时:
			(a) 遍历List<Integer> temp = mapSource.get([target[i]]), 找到temp中可交换的索引位置exchange = temp.get(j), 并记录当前temp中的索引位置j;
			(b) 交换source[i]和source[j]的值;
			(c) 删除source[i]在mapSource中的当前所以位置i;
			(d) 更新source[exchange]在mapsource中的位置, 将source[exchange]当前的i值更新为exchange;

JAVA 代码

      public int minimumHammingDistance(int[] source, int[] target, int[][] allowedSwaps) {
        //Map记录source中的每个值所在的索引位置, 这里有一点, 一个值是可以重复出现的
        Map<Integer,List> mapSource = new HashMap<>();
        for(int i=0;i<source.length;i++){
            if(mapSource.containsKey(source[i])){
                List<Integer> temp = new ArrayList<>(mapSource.get(source[i]));
                temp.add(i);
                mapSource.put(source[i], temp);
            }else {
                List<Integer> temp = new ArrayList<>();
                temp.add(i);
                mapSource.put(source[i], temp);
            }
        }
        //====建立并查集
        int[] UF = new int[source.length];
        for(int i=0;i<UF.length;i++){
            UF[i] = i;
        }
        for(int i=0;i<allowedSwaps.length;i++){
            int val1 = allowedSwaps[i][0];
            int val2 = allowedSwaps[i][1];
            union(UF, val1, val2);
        }
        //====并查集
        int index = 0;
        int exchange = 0;
        int tempVal = 0;
        for(int i=0;i<target.length;i++){
            if(target[i]!=source[i]){
                if(mapSource.containsKey(target[i])){
                    List<Integer> temp = mapSource.get(target[i]);
                    index = -1;
                    exchange = -1;
                    for(int j=0;j<temp.size();j++){
                        if(isConnect(UF, i, temp.get(j))){
                            exchange = temp.get(j);
                            index = j;
                            break;
                        }
                        
                    }
                    if(exchange!=-1){
                        //交换source中索引位置i和exchange对应的值
                        tempVal = source[exchange];
                        source[exchange] = source[i];
                        source[i] = tempVal;
                        //交换后, 删除值target[i]在mapSource中的index对应的位置索引
                        temp.remove(index);
                        mapSource.put(target[i], temp);
                        //交换位置后,要更新交换后mapSource中值的对应索引位置, 将source[exchange]对应在mapSource中i的值更新为exchange
                        temp = new ArrayList<>(mapSource.get(source[exchange]));
                        for(int k=0;k<temp.size();k++){
                            if(temp.get(k)==i){
                                temp.set(k, exchange);
                            }
                        }
                        mapSource.put(source[exchange], temp);
                    }
                }
            }else {
                //当前位置的target[i]和source[i]相等时, 删除source[i]在mapSource中对应的i值
                List<Integer> temp = new ArrayList<>(mapSource.get(source[i]));
                for(int k=0;k<temp.size();k++){
                    if(temp.get(k)==i){
                        temp.remove(k);
                        break;
                    }
                }
                mapSource.put(source[i], temp);
            }
        }

        int ans = 0;
        for(int i=0;i<source.length;i++){
            if(source[i]!=target[i]){
                ans++;
            }
        }
        return ans;
    }

    private int findParent(int[] UF, int val){
        return UF[val] = UF[val]==val?val:findParent(UF, UF[val]);
    }

    private void union(int[] UF, int val1, int val2){
        int parent1 = findParent(UF, val1);
        int parent2 = findParent(UF, val2);
        UF[parent1] = parent2;
        return ;
    }

    private boolean isConnect(int[] UF, int val1, int val2){
        return findParent(UF, val1)==findParent(UF, val2);
    }

踩过的坑

下面的坑都是我踩过的:

1) 没看清题目中, 只允许在source中交换两个不同位置的值。
本人却交换了target数组中两个不同位置的值

2) 题目没有指定source中的每一个值值出现一次, 处于思想上的懒惰,
一开始当作每个值只出现一次的情况进行解答。
但是, source中, 一个值是可以重复出现的。

3) 一开始,当交换两个不同位置的值后, 没有去更新交换后的值对应索引位置,
导致索引位置的延滞性,致使下次交换时的索引位置出错。
所以,在用map记录source中的每个值的索引位置时, 
当交换两个值(source[i]和source[j])的位置后, 
要在map中更新source[i]和source[j]的索引值;

4) 当target[i]和source[i]的值相等时, 
要去map中将值source[i]对应的当前索引位置i删除,
否则导致下次交换时的索引位置出错;
评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值