1722 - 執行交換操作後的最小漢明距離 - 並查集 - 雜湊 -貪心 - DFS
歡迎關注更多精彩
關注我,學習常用演算法與資料結構,一題多解,降維打擊。
文章目錄
題目描述
[1722] 執行交換操作後的最小漢明距離
https://leetcode-cn.com/problems/minimize-hamming-distance-after-swap-operations/
給你兩個整數陣列 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 間的 最小漢明距離 。
示例 1:
輸入:source = [1,2,3,4], target = [2,1,4,5], allowedSwaps = [[0,1],[2,3]]
輸出:1
解釋:source 可以按下述方式轉換:
- 交換下標 0 和 1 指向的元素:source = [2,1,3,4]
- 交換下標 2 和 3 指向的元素:source = [2,1,4,3]
source 和 target 間的漢明距離是 1 ,二者有 1 處元素不同,在下標 3 。
示例 2:
輸入:source = [1,2,3,4], target = [1,3,2,4], allowedSwaps = []
輸出:2
解釋:不能對 source 執行交換操作。
source 和 target 間的漢明距離是 2 ,二者有 2 處元素不同,在下標 1 和下標 2 。
示例 3:
輸入:source = [5,1,2,4,3], target = [1,5,4,2,3], allowedSwaps = [[0,4],[4,2],[1,3],[1,4]]
提示:
n == source.length == target.length
1 <= n <= 10^5
1 <= source[i], target[i] <= 10^5
0 <= allowedSwaps.length <= 10^5
allowedSwaps[i].length == 2
0 <= ai, bi <= n - 1
ai != bi
- 並查集
- 圖連通塊
- DFS
題目剖析&資訊挖掘
題目考查的是圖連通塊的查詢,可以使用並查集或者深度優先遍歷實現。並查集更加簡便,我使用的是並查集。
並查集還不會?趕緊來學習吧並查集學習資料
解題思路
方法一 並查集+雜湊+貪心
分析
首先,可以發現一個規律。
交換關係具有傳遞性。即如果a與b可以交換,b與c可以交換,那麼a,b,c可以兩兩相互交換。(自己模擬一下就知道)
那麼對於給定allowedSwaps可以找出相互關係的一組資料(在圖中稱為連通塊)。
一個連通塊與其他連通塊是不能進行交換。
題目目要求漢明距離最小,那麼我們希望通過交換可以使最多的資料都對應上。
找到一個連通快後,利用雜湊演算法去查詢最大配對數(pairs)。
len(target)-pairs即為答案
思路
func minimumHammingDistance(source []int, target []int, allowedSwaps [][]int) int {
us := UnionFindSet{}
us.InitUnionSet(len(target)+2)
// 合併所人相關資料
for _, v := range allowedSwaps{
us.Union(v[0], v[1])
}
set := make([][]int, len(target)) // 儲存以為個結點為根的集合,不一定都有元素
// 將一個連通塊內的資料收集起來
for i:=0;i<len(target);i++ {
f := us.FindV2(i)
if set[f] == nil {set[f] = []int{}}
set[f] = append(set[f], i)
}
sum :=0
// 查詢最大配對數
for _, s := range set {
sum += getSameNum(source, target, s)
}
return len(target)-sum // 總數-配對數
}
注意
- 數字是有重複的。
- 查詢的是最大配對數,不是最終結果。
知識點
- 並查集
- 雜湊
- 連通塊
- 貪心
複雜度
- 時間複雜度:O(n)
- 空間複雜度:O(n)
參考
程式碼實現
/*
並查集,判連通用
*/
type UnionFindSet struct {
father []int // 儲存結點的父親
nodeNum int // 總結點個數
}
func (us *UnionFindSet) InitUnionSet(n int) {
us.nodeNum = n+1 // 不加也可以,有人喜歡以0開頭
us.father = make([]int, us.nodeNum)
for i, _ := range us.father {
us.father[i] = i
}
}
//合併結點
func (us *UnionFindSet) Union(x, y int) bool {
x = us.FindV2(x)
y = us.FindV2(y)
if x == y {
return false
}
us.father[x] = y
return true
}
func (us *UnionFindSet) FindV2(x int) int {
root := x // 儲存好路徑上的頭結點
for us.father[root] != root {
root = us.father[root]
}
/*
從頭結點開始一直往根上遍歷
把所有結點的father直接指向root。
*/
for us.father[x] != x {
// 一定要先儲存好下一個結點,下一步是要對us.father[x]進行賦值
temp := us.father[x]
us.father[x] = root
x = temp
}
return root
}
func getSameNum(source []int, target []int, set []int) int {
cnt := make(map[int]int)
for _, v := range set {
cnt[source[v]]++
}
sum :=0
for _, v := range set {
if cnt[target[v]]>0{
sum++
cnt[target[v]]--
}
}
return sum
}
func minimumHammingDistance(source []int, target []int, allowedSwaps [][]int) int {
us := UnionFindSet{}
us.InitUnionSet(len(target)+2)
for _, v := range allowedSwaps{
us.Union(v[0], v[1])
}
set := make([][]int, len(target)) // 儲存以為個結點為根的集合,不一定都有元素
for i:=0;i<len(target);i++ {
f := us.FindV2(i)
if set[f] == nil {set[f] = []int{}}
set[f] = append(set[f], i)
}
sum :=0
for _, s := range set {
sum += getSameNum(source, target, s)
}
return len(target)-sum
}
相關題目
並查集學習資料 這裡都總結好了
本人碼農,希望通過自己的分享,讓大家更容易學懂計算機知識。