圖論之并查集——含例題

目錄

介紹

秩是什么

例子——快速入門

例題

使用路徑壓縮,不使用秩合并

使用路徑壓縮和秩合并

無向圖和有向圖


介紹

并查集是一種用于 處理不相交集合的合并與查詢問題的數據結構。它主要涉及以下基本概念和操作:
基本概念:
  • 集合:并查集中的集合是由一組元素組成的,這些元素具有相同的屬性或特征,集合之間相互不相交。
  • 代表元素:每個集合都有一個代表元素,用于標識該集合。集合中的其他元素都可以通過一定的關系與代表元素相連。
基本操作:
  • 初始化:將每個元素都初始化為一個獨立的集合,每個集合的代表元素就是該元素本身。
  • 合并:將兩個不同集合合并為一個集合。通常是將一個集合的代表元素連接到另一個集合的代表元素上,使得兩個集合成為一個更大的集合。
  • 查找:查找某個元素所在集合的代表元素。通過不斷地沿著元素的父指針追溯,最終找到代表元素,從而確定該元素屬于哪個集合。
并查集通常使用數組來實現,數組的下標表示元素,數組中存儲的是該元素的父元素或代表元素的下標。在一些復雜的應用場景中,為了提高并查集的操作效率,還會采用路徑壓縮和按秩合并等優化策略。
并查集在 圖論、數據分類、連通性問題等領域有廣泛的應用。例如,在處理圖的連通分量問題時,可以使用并查集來快速判斷兩個頂點是否屬于同一個連通分量,以及合并不同的連通分量。

秩是什么

定義:秩可以看作是樹的高度的一個估計值。在并查集的初始化階段,每個元素都自成一個集合,此時集合的秩通常被初始化為 1,表示單個元素構成的樹高度為 1。
作用: 在合并兩個集合時,通過比較兩個集合的秩來決定如何合并,以盡量保持樹的平衡性,避免出現退化的樹結構(即高度過高的樹,會導致查找操作的時間復雜度增加)。
按秩合并策略:
  • 當合并兩個集合時,比較它們的秩。如果兩個集合的秩不同,將秩較小的集合合并到秩較大的集合中。這樣做的原因是,將較小的樹連接到較大的樹上,對整體樹的高度影響較小,有助于保持樹的平衡性。例如,一個秩為 2 的樹和一個秩為 3 的樹合并,會將秩為 2 的樹連接到秩為 3 的樹下面,合并后新樹的秩不變,仍為 3。
  • 如果兩個集合的秩相同,那么可以任選一個集合作為合并的目標集合,并將另一個集合合并到該集合中。在這種情況下,合并后新集合的秩會增加 1。例如,兩個秩都為 2 的樹合并,合并后新樹的秩變為 3。
通過使用秩和按秩合并策略,可以有效地降低并查集操作的時間復雜度,使得在大多數情況下,查找和合并操作都能在接近常數時間內完成。

例子——快速入門

假設有一群人,他們之間存在著不同的朋友關系。我們把每個人看作一個節點,朋友關系看作是連接節點的邊,現在需要判斷兩個人是否在同一個朋友圈中,以及統計朋友圈的數量。
  • 初始化:假設有 5 個人,分別用編號 0 - 4 表示。一開始,每個人都屬于自己獨立的朋友圈,即每個節點的父節點都是它自己。可以用一個數組parent來表示,parent[i]表示節點i的父節點,初始化為parent = [0, 1, 2, 3, 4]。
  • 合并朋友圈:
    • 已知 0 和 1 是朋友,通過union操作合并他們所在的集合。找到 0 和 1 的根節點,即 0 和 1 本身,將 1 的父節點設置為 0,此時parent = [0, 0, 2, 3, 4],表示 0 和 1 在同一個朋友圈中。
    • 接著,2 和 3 是朋友,進行同樣的合并操作,將 3 的父節點設置為 2,parent = [0, 0, 2, 2, 4]。
    • 然后,1 和 3 是朋友,再次合并。先找到 1 的根節點是 0,3 的根節點是 2,將 2 的父節點設置為 0,parent = [0, 0, 0, 0, 4],此時 0、1、2、3 都在同一個朋友圈中。
  • 查找:
    • 要判斷 4 和 3 是否在同一個朋友圈,通過find操作查找 4 的根節點是 4,3 的根節點是 0,根節點不同,所以 4 和 3 不在同一個朋友圈。
    • 要判斷 0 和 2 是否在同一個朋友圈,查找 0 和 2 的根節點都是 0,根節點相同,所以 0 和 2 在同一個朋友圈。
  • 統計朋友圈數量:最后,通過遍歷parent數組,統計根節點的數量,即不同的代表元素的數量,就可以得到朋友圈的數量。在這個例子中,有兩個不同的根節點 0 和 4,所以朋友圈數量為 2。
package mainimport "fmt"// UnionFind 定義并查集結構體
type UnionFind struct {parent []int // parent 切片用于存儲每個元素的父節點,初始時每個元素的父節點是其自身// 在合并兩個集合時,通過比較兩個集合的秩來決定如何合并,以盡量保持樹的平衡性,避免出現退化的樹結構(即高度過高的樹,會導致查找操作的時間復雜度增加)。rank  []int // rank 切片用于記錄每個集合的秩(通常是樹的高度)count int   // 朋友圈的數量
}// NewUnionFind 初始化并查集
func NewUnionFind(n int) *UnionFind {parent := make([]int, n)rank := make([]int, n)for i := range parent {parent[i] = irank[i] = 1}return &UnionFind{parent: parent,rank:   rank,count:  n,}
}// Find 查找元素所在集合的代表元素
func (uf *UnionFind) Find(x int) int {// 如果元素x的父節點(parent[x])不是它自己,就遞歸的查找它(parent[x]元素)的父節點if uf.parent[x] != x {uf.parent[x] = uf.Find(uf.parent[x])}return uf.parent[x]
}// Union 合并兩個元素所在的集合
func (uf *UnionFind) Union(x, y int) {rootX := uf.Find(x)rootY := uf.Find(y)if rootX == rootY {return}if uf.rank[rootX] < uf.rank[rootY] {rootX, rootY = rootY, rootX}uf.parent[rootY] = rootX         // 更改 rootY 的父節點為 rootXuf.rank[rootX] += uf.rank[rootY] // 更改 rootX 的秩uf.count--                       // 朋友圈數量--
}// GetCount 獲取連通分量的數量
func (uf *UnionFind) GetCount() int {return uf.count
}func main() {// 假設有 5 個人n := 5uf := NewUnionFind(n)// 合并操作,模擬朋友關系uf.Union(0, 1)uf.Union(2, 3)uf.Union(1, 3)// 判斷 4 和 3 是否在同一個朋友圈sameCircle1 := uf.Find(4) == uf.Find(3)fmt.Printf("4 和 3 是否在同一個朋友圈: %v\n", sameCircle1)// 判斷 0 和 2 是否在同一個朋友圈sameCircle2 := uf.Find(0) == uf.Find(2)fmt.Printf("0 和 2 是否在同一個朋友圈: %v\n", sameCircle2)// 統計朋友圈的數量circleCount := uf.GetCount()fmt.Printf("朋友圈的數量: %d\n", circleCount)// 4 和 3 是否在同一個朋友圈: false// 0 和 2 是否在同一個朋友圈: true// 朋友圈的數量: 2
}

例題

在并查集的實現中,rank?數組(或類似用于記錄秩的機制)并不是必需的,有些題目里的并查集沒有使用?rank?數組主要有以下原因:

簡化實現:對于一些簡單的問題場景,不需要通過按秩合并來優化并查集的性能,僅使用路徑壓縮就可以滿足時間復雜度要求。此時可以省略?rank?數組,代碼實現會更簡潔。比如在一些數據規模較小或者對時間復雜度要求不高的問題中,單純的路徑壓縮就能讓并查集的操作效率足夠高。

采用其他優化方式:有些并查集的實現可能不使用?rank?數組來記錄秩,而是采用其他方式來優化合并操作。例如,記錄每個集合的大小,在合并時將較小的集合合并到較大的集合中,這種方法也能在一定程度上避免樹結構的退化,提高查找和合并的效率。

使用路徑壓縮,不使用秩合并

// 使用路徑壓縮,不使用秩合并package maintype UnionFind struct {parent []int
}func NewUnionFind(n int) *UnionFind {parent := make([]int, n)for i := range parent {parent[i] = i}return &UnionFind{parent: parent,}
}// Find 查找
func (uf *UnionFind) Find(x int) int {if uf.parent[x] != x {uf.parent[x] = uf.Find(uf.parent[x])}return uf.parent[x]
}// Union 合并
func (uf *UnionFind) Union(x, y int) {rootX := uf.Find(x)rootY := uf.Find(y)uf.parent[rootY] = rootX
}// IsConnected 判斷兩個元素是否在同一個集合中
func (uf *UnionFind) IsConnected(x, y int) bool {return uf.Find(x) == uf.Find(y)
}

相應的例題:

力扣:547. 省份數量(并查集,也可以用dfs、bfs)

力扣:684. 冗余連接(并查集)

使用路徑壓縮和秩合并

// 使用路徑壓縮和秩合并(優化并查集的性能)package main// UnionFind 定義并查集結構體
type UnionFind struct {parent []intrank   []int
}// NewUnionFind 初始化并查集
func NewUnionFind(n int) *UnionFind {parent := make([]int, n)rank := make([]int, n)for i := range parent {parent[i] = irank[i] = 1}return &UnionFind{parent: parent,rank:   rank,}
}// Find 查找元素所在集合的代表元素,使用路徑壓縮
func (uf *UnionFind) Find(x int) int {if uf.parent[x] != x {uf.parent[x] = uf.Find(uf.parent[x])}return uf.parent[x]
}// Union 合并兩個元素所在的集合,使用按秩合并
func (uf *UnionFind) Union(x, y int) {rootX := uf.Find(x)rootY := uf.Find(y)if rootX == rootY {return}if uf.rank[rootX] < uf.rank[rootY] {rootX, rootY = rootY, rootX}uf.parent[rootY] = rootXuf.rank[rootX] += uf.rank[rootY]
}// IsConnected 判斷兩個元素是否在同一個集合中
func (uf *UnionFind) IsConnected(x, y int) bool {return uf.Find(x) == uf.Find(y)
}

相應的例題:

力扣:1584. 連接所有點的最小費用(Kruskal算法、最小生成樹、并查集)

無向圖和有向圖

并查集在無向圖中的應用更為直接和常見。(當然,在一些有向圖的問題中也能通過適當的轉化和處理來發揮作用)

相應的例題:

力扣:2101. 引爆最多的炸彈(有向圖)

問:這道題為什么不能用并查集?

答:注意本題是有向圖。例如炸彈 0 可以引爆炸彈 2,炸彈 1 可以引爆炸彈 2,對應有向邊 0→2,1→2,那么正確答案是 2。如果用并查集做的話,會把 0,1,2 三個點合并起來,計算出錯誤的答案 3。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/bicheng/76742.shtml
繁體地址,請注明出處:http://hk.pswp.cn/bicheng/76742.shtml
英文地址,請注明出處:http://en.pswp.cn/bicheng/76742.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

【數學建模】(智能優化算法)天牛須算法(Beetle Antennae Search, BAS)詳解與Python實現

天牛須算法(Beetle Antennae Search, BAS)詳解與Python實現 文章目錄 天牛須算法(Beetle Antennae Search, BAS)詳解與Python實現1. 引言2. 算法原理2.1 基本思想2.2 數學模型 3. Python實現4.實測效果測試1. Michalewicz函數的最小化測試2. Goldstein-Price函數的約束最小化 5…

【家政平臺開發(42)】筑牢家政平臺安全防線:安全測試與漏洞修復指南

本【家政平臺開發】專欄聚焦家政平臺從 0 到 1 的全流程打造。從前期需求分析,剖析家政行業現狀、挖掘用戶需求與梳理功能要點,到系統設計階段的架構選型、數據庫構建,再到開發階段各模塊逐一實現。涵蓋移動與 PC 端設計、接口開發及性能優化,測試階段多維度保障平臺質量,…

學習筆記八——內存管理相關

&#x1f4d8; 目錄 內存結構基礎&#xff1a;棧、堆、數據段Rust 的內存管理機制&#xff08;對比 C/C、Java&#xff09;Drop&#xff1a;Rust 的自動清理機制Deref&#xff1a;為什么 *x 能訪問結構體內部值Rc&#xff1a;多個變量“共享一個資源”怎么辦&#xff1f;Weak&…

ReliefF 的原理

&#x1f31f; ReliefF 是什么&#xff1f; ReliefF 是一種“基于鄰居差異”的特征選擇方法&#xff0c;用來評估每個特征對分類任務的貢獻大小。 它的核心問題是&#xff1a; “我怎么知道某個特征是不是重要&#xff1f;是不是有能力把不同類別的數據區分開&#xff1f;” 而…

?asm匯編源代碼之-漢字點陣字庫顯示程序源代碼下載?

漢字點陣字庫顯示程序 源代碼下載 文本模式下顯示16x16點陣漢字庫內容的程序(標準16x16字庫需要使用CHGHZK轉換過后才能使用本程序正常顯示) 本程序需要調用file.asm和string.asm中的子程序,所以連接時需要把它們連接進來,如下 C:\> tlink showhzk file string 調用參…

【已更新完畢】2025泰迪杯數據挖掘競賽B題數學建模思路代碼文章教學:基于穿戴裝備的身體活動監測

基于穿戴裝備的身體活動監測 摘要 本研究基于加速度計采集的活動數據&#xff0c;旨在分析和統計100名志愿者在不同身體活動類別下的時長分布。通過對加速度數據的處理&#xff0c;活動被劃分為睡眠、靜態活動、低強度、中等強度和高強度五類&#xff0c;進而計算每個志愿者在…

Ubuntu24.04裝機安裝指南

文章目錄 Ubuntu24.04裝機安裝指南一、分區說明二、基礎軟件三、使用fcitx5配置中文輸入法四、安裝搜狗輸入法【**不推薦**】1. 安裝fcitx2. 安裝輸入法 五、禁用/home目錄下自動生成文件夾六、更新軟件源1. 針對**新配置方式**的清華源替換方法2. 針對**老配置方式**的清華源替…

互聯網三高-數據庫高并發之分庫分表ShardingJDBC

1 ShardingJDBC介紹 1.1 常見概念術語 ① 數據節點Node&#xff1a;數據分片的最小單元&#xff0c;由數據源名稱和數據表組成 如&#xff1a;ds0.product_order_0 ② 真實表&#xff1a;再分片的數據庫中真實存在的物理表 如&#xff1a;product_order_0 ③ 邏輯表&#xff1a…

BM25、BGE以及text2vec-base-chinese的區別

BM25、BGE以及text2vec-base-chinese的區別 BM25 原理:BM25(Best Matching 25)是一種基于概率檢索模型的算法,它通過考慮查詢詞與文檔之間的匹配程度、文檔的長度等因素,來計算文檔對于查詢的相關性得分。具體來說,它會給包含查詢詞次數較多、文檔長度適中的文檔更高的分…

Python中try用法、內置異常類型與自定義異常類型拓展

目錄 try介紹與語法格式try具體使用案例except的異常類型簡介案例內置的常見異常類型自定義異常類型繼承關系用途 注意事項 try介紹與語法格式 在 Python 里&#xff0c;try 語句主要用于異常處理&#xff0c;其作用是捕獲并處理代碼運行期間可能出現的異常&#xff0c;避免程…

【第41節】windows的中斷與異常及異常處理方式

目錄 一、中斷與異常處理 1.1 中斷與異常 1.2 IDT 1.3 異常的概念 1.4 異常分類 二、windows異常處理方式 2.1 概述 2.2 結構化異常處理 2.3 向量化異常處理之VEH 2.4 向量化異常處理之VCH 2.5 默認的異常處理函數 2.6 如何手動安裝 SEH 節點 2.7 異常處理的優先級…

分布式日志治理:Log4j2自定義Appender寫日志到RocketMQ

&#x1f9d1; 博主簡介&#xff1a;CSDN博客專家&#xff0c;歷代文學網&#xff08;PC端可以訪問&#xff1a;https://literature.sinhy.com/#/?__c1000&#xff0c;移動端可微信小程序搜索“歷代文學”&#xff09;總架構師&#xff0c;15年工作經驗&#xff0c;精通Java編…

【HTML】html文件

HTML文件全解析&#xff1a;搭建網頁的基石 在互聯網的廣袤世界里&#xff0c;每一個絢麗多彩、功能各異的網頁背后&#xff0c;都離不開HTML文件的默默支撐。HTML&#xff0c;即超文本標記語言&#xff08;HyperText Markup Language&#xff09;&#xff0c;作為網頁創建的基…

oracle命令上下左右鍵無法使用如何解決?

1、問題如圖 2、解決辦法 (1) 安裝readline yum -y install readline* &#xff08;2&#xff09;安裝 rlwrap ##下載 wget http://files.cnblogs.com/files/killkill/rlwrap-0.30.tar.gz.zip ##解壓 tar -xzvf rlwrap-0.30.tar.gz.zip ##編譯安裝 ./configure make &&…

vue事假機制都有哪些

Vue 的事件機制主要包含以下幾種類型和方式&#xff0c;可以分為組件內部事件、父子組件通信事件、原生 DOM 事件封裝、修飾符增強等&#xff0c;下面詳細分類介紹&#xff1a; 一、DOM 事件綁定&#xff08;最基礎的事件&#xff09; 使用 v-on&#xff08;或簡寫 &#xff0…

系統編程2(消息隊列)

? 消息隊列概念 Linux系統中消息隊列&#xff08;Message Queue&#xff09;是進程間通信的一種方式&#xff0c;這種通信機制的好處是可以傳輸指定類型(用戶可以自行定義)的數據&#xff0c;相同類型的數據根據到達順序在隊列中進行排隊。 當然&#xff0c;不同類型的數據不…

Pytorch深度學習框架60天進階學習計劃 - 第41天:生成對抗網絡進階(二)

Pytorch深度學習框架60天進階學習計劃 - 第41天&#xff1a;生成對抗網絡進階&#xff08;二&#xff09; 7. 實現條件WGAN-GP # 訓練條件WGAN-GP def train_conditional_wgan_gp():# 用于記錄損失d_losses []g_losses []# 用于記錄生成樣本的多樣性&#xff08;通過類別分…

python 微博爬蟲 01

起因&#xff0c; 目的: ?下載單個視頻&#xff0c;完成。? 獲取某用戶的視頻列表&#xff0c;完成。剩下的就是&#xff0c; 根據視頻列表&#xff0c;逐個下載視頻&#xff0c;我沒做&#xff0c;沒意思。獲取視頻的評論&#xff0c;以后再說。 關鍵點記錄: 1. 對一個視…

Servlet、HTTP與Spring Boot Web全面解析與整合指南

目錄 第一部分&#xff1a;HTTP協議與Servlet基礎 1. HTTP協議核心知識 2. Servlet核心機制 第二部分&#xff1a;Spring Boot Web深度整合 1. Spring Boot Web架構 2. 創建Spring Boot Web應用 3. 控制器開發實踐 4. 請求與響應處理 第三部分&#xff1a;高級特性與最…

vue中根據html動態渲染內容2.0

上次使用的是p標簽用的contenteditable代替的可編輯的input&#xff0c;最后實現還是選擇了用el-input的textarea方式。 一開始考慮的是需要根據用戶輸入自動撐開輸入框&#xff0c;所以選擇了p標簽可編輯。 最后發現還是el-input會更好一點&#xff0c;只不過需要處理輸入框撐…