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删除,
否则导致下次交换时的索引位置出错;