1. 程式人生 > 其它 >1722 - 執行交換操作後的最小漢明距離 - 並查集 - 雜湊 -貪心 - DFS

1722 - 執行交換操作後的最小漢明距離 - 並查集 - 雜湊 -貪心 - DFS

技術標籤:leetcodedfs貪心演算法雜湊

歡迎關注更多精彩
關注我,學習常用演算法與資料結構,一題多解,降維打擊。

文章目錄

題目描述

[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]]

輸出:0

提示:

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

Related Topics
  • 並查集
  • 圖連通塊
  • 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
}

相關題目

並查集學習資料 這裡都總結好了


本人碼農,希望通過自己的分享,讓大家更容易學懂計算機知識。

在這裡插入圖片描述