圖論:并查集

入門

久聞并查集的大名,今天來一探究竟,到底什么是并查集,并查集有什么用?

并查集(Disjoint Set Union, DSU)是一種處理不相交集合的合并及查詢問題的數據結構。

其實并查集的作用主要就有兩個:

1、將兩個元素添加到同一個集合

2、判斷兩個元素是否在同一個集合內

碰到諸如此類的問題,就可以條件反射的去想到用并查集來解決了。

首先就是預處理的操作了只需要將所有的點連向自己即可:

void pre_handle()
{for(int i=0;i<n;i++) father[i] = i;
} 

然后就是添加函數和判斷函數了,這兩個函數都基于查找函數,要首先查找到兩個點的根節點是誰

普通的查找函數:查找函數是基于遞歸來實現的,就是不斷的遞歸去找父節點的父節點知道根節點

int find(int x)
{if(father[x] == x) return x;return find(father[x]);
}

但是這樣一直遞歸無疑是一個非常浪費時間的過程,每次查找一個點的根節點的時候都要從這個點開始遞歸到根節點,其實這個過程中做了很多重復的東西,在一個點遞歸完之后,他的所有父節點是不就就無需再遞歸了呢,所以就要考慮能不能把路徑壓縮,其實只需要在遞歸的過程中,將每個點的父節點都存為根節點,這樣在下一次查找的時候,既可以直接根據這個點存的值直接找到該點的根節點了,比如一開始的1->2->3->4 當find(1)的時候在遞歸的過程中就變為了1->4, 2->4, 3->4 這樣在下一次找2的根節點的時候就直接遞歸一次即可返回4,這個操作在較深的圖中是很有用的!

int find(int x)
{if(father[x] == x) return x;father[x] = find(father[x]);//直接將根節點賦值給x的父節點 相當于深度變為了兩層return father[x];
}

這樣是不是很妙呢?有了find函數,接下來的添加(join)和判斷(is_same)函數就很容易實現了。

join:

void join(int x,int y)
{x = find(x);//x直接存x的根節點y = find(y);//y同樣存y的根節點if(x==y) return ;//如果x和y的根節點相同就說明本來就在同一個集合內father[x] = y;//讓x的根節點指向y的根節點 就說明此時兩個集合已經聯通了
}

is_same:

bool is_same(int x,int y)
{x = find(x);y = find(y);return x==y ? true : false;
}

理論完成,下面開始做幾道題來練練手吧!

尋找存在的路徑?

題目鏈接:尋找存在的路徑

這是并查集的基礎模板的應用,按要求將聯通的兩個點加入到同一個集合中即可。

#include <bits/stdc++.h>
using namespace std;
#define int long long 
#define endl '\n'
#define IOS ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
#define pii pair<int,int>
#define fi first
#define se second
const int N = 110;
vector<int> father(N);
int n,m;
void pre_handle()
{for(int i=1;i<=n;i++) father[i] = i;
} 
int find(int x)
{if(x == father[x]) return x;father[x] = find(father[x]);return father[x];
}
void join(int x,int y)
{x = find(x);y = find(y);if(x == y) return ;father[x] = y;//兩個集合聯通
}
bool is(int x,int y)
{x = find(x);y = find(y);return x==y ? true : false;
}
void solve()
{cin>>n>>m;pre_handle();for(int i=1;i<=m;i++){int u,v;cin>>u>>v;join(u,v);}int u,v;cin>>u>>v;if(is(u,v)) cout<<"1"<<endl;else cout<<"0"<<endl;
}signed main()
{IOSint T=1;
//	cin>>T;while(T--) solve(); return 0;
} 

冗余的邊?

題目鏈接:冗余的邊

這道題雖然是看著像一個復雜的樹或圖問題,其實本質上就是判斷當前的邊所連接的兩個點之前可達不可達,如果可達(聯通)的話那么此時的邊就是冗余的了,又因為聯通無環無向圖就是一顆樹所以只有一條冗余的邊。那么只需要用并查集去判斷是否可達(在一個集合內)即可。

#include <bits/stdc++.h>
using namespace std;
#define int long long 
#define endl '\n'
#define IOS ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
#define pii pair<int,int>
#define fi first
#define se second
const int N = 1010;
vector<int> father(N);
int n;
int ans1,ans2;
void pre_handle()
{for(int i=1;i<=n;i++) father[i] = i;
} 
int find(int x)
{if(x == father[x]) return x;father[x] = find(father[x]);return father[x];
}
void join(int x,int y)
{x = find(x);y = find(y);if(x == y) return ;father[x] = y;//兩個集合聯通
}
bool is(int x,int y)
{x = find(x);y = find(y);return x==y ? true : false;
}
void solve()
{cin>>n;pre_handle();for(int i=1;i<=n;i++){int u,v;cin>>u>>v;if(is(u,v))//只要u和v在同一個集合中(可達 聯通)就說明此時的邊冗余了{ans1 = u;ans2 = v;}else join(u,v);}cout<<ans1<<" "<<ans2<<endl;
}signed main()
{IOSint T=1;
//	cin>>T;while(T--) solve(); return 0;
} 

冗余的邊II?

題目鏈接:冗余的邊II

這道題原本是一個有向樹,因為多了一條冗余的邊導致成為了一個圖,所以我們就要判斷哪些情況是導致冗余的原因,大體上分為兩種

1.有向樹的性質就是除了根節點的入度為0,其他的節點入度為1,入度為2就說明不是樹了

2.入度也可能都是1,但是如果構成環了也就不是樹了

但是第一種情況又可以細分,入度為2可能只有其中一條邊是冗余的(入度為0的根節點與其相連的時候 這條邊就不是一個冗余的邊)所以要在入度為2的時候判斷一下是否能刪

可以利用并查集來判斷是否有環!

  1. 題目特殊條件

    • 圖是由"有向樹添加一條邊"構成的

    • 這意味著原始結構是一棵有向樹(n-1條邊)

    • 添加一條邊后(共n條邊),只可能產生兩種違規情況:
      a) 某個節點入度變為2
      b) 形成一個環

  2. 并查集的適用性

    • 雖然并查集通常用于無向圖,但這里我們關注的是"連接性"而非方向

    • 判斷是否有環時,方向不重要(有向環必然包含無向環)

    • 判斷連通性時,方向也不重要(我們只需知道節點是否相連)

  3. 方向信息的處理

    • 入度統計單獨處理(用indegree數組)

    • 并查集只負責檢測環和連通性

    • 兩個部分各司其職,共同解決問題

#include <bits/stdc++.h>
using namespace std;
#define int long long 
#define endl '\n'
#define IOS ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
#define pii pair<int,int>
#define fi first
#define se second
const int N = 1010;
vector<int> father(N),indegree(N,0),a;//定義并查集數組 入度數組 以及入度為2的數組
vector<pii> e(1);//存所有的兩兩有連接的邊 不需要建樹 只需要用入度和并查集統計邊即可
int n;
void init()//初始化并查集
{for(int i=1;i<=n;i++) father[i] = i;
}
int find(int x)
{if(x == father[x]) return x;return father[x] = find(father[x]);
}
void join(int x,int y)
{x = find(x);y = find(y);if(x == y) return ;father[x] = y;
}
bool is(int x,int y)
{x = find(x);y = find(y);return x==y ? true : false;
}
bool istree(int x)//刪除x這條邊之后是否是一棵樹
{//判斷連通性時,方向并不重要(我們只需知道節點是否相連)init();for(int i=1;i<=n;i++){if(i == x) continue;if(is(e[i].fi,e[i].se)) return false;//成環了 不是有向樹了join(e[i].fi,e[i].se);}return true;
}
void solve()
{cin>>n;init();for(int i=1;i<=n;i++){int u,v;cin>>u>>v;e.push_back({u,v});indegree[v]++;}for(int i=1;i<=n;i++){if(indegree[e[i].se] == 2) a.push_back(i);//將導致入度為2的邊的編號存入}if(a.size()>0)//size==2{if(istree(a[1]))//因為要輸出標準輸入中靠后的一個 所以先判斷a[1]cout<<e[a[1]].fi<<' '<<e[a[1]].se<<endl;else//如果刪除a[1]這條邊的話還是一個圖 那么就一定是另一條邊了(因為只會多出來一條邊)cout<<e[a[0]].fi<<' '<<e[a[0]].se<<endl;}else//第三種情況{init();//找冗余的邊 用并查集 判斷成環的那條邊//判斷是否有環時,方向并不重要(有向環必然包含無向環)for(int i=1;i<=n;i++){if(is(e[i].fi,e[i].se)){cout<<e[i].fi<<' '<<e[i].se<<endl;return ;}elsejoin(e[i].fi,e[i].se);}}
}signed main()
{IOSint T=1;
//	cin>>T;while(T--) solve(); return 0;
} 

總之,并查集應用于無向圖中,目的是看兩個點是否聯通或者兩個集合是否聯通以及將兩個點加入到同一個集合當中構成聯通性。?

這樣就將并查集的兩個主要作用給介紹完了,期待后續的迪杰斯特拉算法吧!

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

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

相關文章

告別靜態文檔!Oracle交互式技術架構圖讓數據庫學習“活“起來

&#x1f5fa;? 當數據庫架構圖學會"互動" 想象一下&#xff0c;你正在學習Oracle數據庫架構&#xff0c;面對密密麻麻的靜態文檔和復雜的組件關系圖&#xff0c;是不是常常感到&#xff1a; 像在迷宮里找路&#xff0c;不知道組件間如何協作&#xff1f;想深入了…

day62-可觀測性建設-全鏈路監控zabbix+grafana

&#x1f31f;監控api接口 &#x1f50d;監控zabbix-api接口 生成API tokens命令行測試 curl -s -X POST -H "Content-Type: application/json-rpc" -d {"jsonrpc": "2.0","method": "host.get","params": {&quo…

通過Deepseek找工作

推送的結果如下,對應的AI提示詞在底部: 計算機方向遠程工作職位匯總 整合全球遠程技術崗位 | 支持全地域遠程辦公 | 涵蓋開發、安全、云計算等方向 覆蓋方向:8+個技術領域 薪資范圍:10K-40K/月 工作模式:100%遠程 遠程技術職位列表 職位名稱 技能要求 經驗要求 薪資…

vscode文件顏色,只顯示自己更改的文件顏色、剛git下來的庫,vscode打開后,顯示所有文件都被修改了

問題&#xff1a;git新的庫&#xff0c;然后我用vscode打開&#xff0c;默認顯示所有的文件都更改了&#xff0c;但是我打開他們修改的對比&#xff0c;沒有顯示任何有被修改的地方&#xff0c;是怎么回事 linux/wsl下這么設置就可以了&#xff1a;git config core.autocrlf in…

基于ENMeval包的MaxEnt模型參數優化總結

MaxEnt模型參數優化1. MaxEnt模型優化&#xff1a;增加RM&#xff0c;降低模型過擬合風險&#xff0c;簡易模型&#xff0c;平滑響應曲線&#xff0c;增強模型可解釋性和轉移性&#xff08;生物入侵&#xff09;2. 默認參數&#xff1a;FCLQHP&#xff0c;RM12.1. 基于優化的 M…

Docker實踐:使用Docker部署blog輕量級博客系統

Docker實踐&#xff1a;使用Docker部署blog輕量級博客系統一、blog系統介紹1.1 blog介紹1.2 個人博客系統介紹1.3 個人博客使用場景二、本地環境介紹2.1 本地環境規劃2.2 本次實踐介紹三、本地環境檢查3.1 檢查Docker服務狀態3.2 檢查Docker版本3.3 檢查docker compose 版本四、…

專題:2025電商增長新勢力洞察報告:區域裂變、平臺壟斷與銀發平權|附260+報告PDF、原數據表匯總下載

原文鏈接&#xff1a;https://tecdat.cn/?p43416 當茂名果農對著鏡頭用方言喊出“荔枝現摘現發”&#xff0c;2小時賣出83萬元&#xff1b;當65歲的上海阿姨通過“子女代付”買到人生第一臺智能冰箱——2025年的電商戰場&#xff0c;正在上演三重革命&#xff1a;新興市場的增…

數字化轉型-AI落地金字塔法則

前言 人工智能必須要跟傳統產業結合&#xff0c;融入傳統產業&#xff0c;才能落地&#xff0c;才能產生巨大的倍增個幾何級效果&#xff01;&#xff01; AI不應該停留在工具層面&#xff0c;AI不僅僅是工具&#xff0c;不僅僅是硬件和軟件&#xff0c;而是軟硬結合。人工智能…

SQL Server 字段類型選型指南:什么數據用什么字段

目錄 一、數值型數據 二、日期與時間數據 三、字符串與文本數據 四、布爾值與狀態碼 五、二進制與文件數據 六、唯一標識符&#xff08;GUID&#xff09; 七、枚舉與代碼表設計 八、存儲優化小結 九、總結 在數據庫設計中&#xff0c;字段類型&#xff08;數據類型&am…

酷暑來襲,科技如何讓城市清涼又潔凈?

烈日下的身影&#xff0c;不該被“炙烤”的擔當又是一年盛夏&#xff0c;城市的血管在高溫下脈動&#xff0c;柏油馬路仿佛要融化&#xff0c;空氣中彌漫著灼熱的氣息。此刻&#xff0c;你是否曾留意過那些身影&#xff1f;在烈日下&#xff0c;他們依舊堅守崗位&#xff0c;用…

傳統框架與減震樓蓋框架地震動力響應分析與有限元模擬

傳統框架與減震樓蓋框架地震動力響應分析與有限元模擬 前些天發現了一個巨牛的人工智能學習網站,通俗易懂,風趣幽默,忍不住分享一下給大家,覺得好請收藏。點擊跳轉到網站。 摘要 本文針對傳統鋼框架和減震樓蓋鋼框架兩種結構體系,建立了水平地震作用下的動力學模型,推…

Java集合去重

? 方式一&#xff1a;TreeSet Comparator最優雅的一種&#xff0c;適用于對象中某個字段唯一的去重&#xff08;如 partyAId&#xff09;List<PartyACompanyVO> result contractDOS.stream().map(contract -> {PartyACompanyVO vo new PartyACompanyVO();vo.setPa…

Qt字符串處理與正則表達式應用

一、Qt字符串處理基礎 在Qt應用程序開發中&#xff0c;字符串處理是一項常見且重要的任務。Qt提供了強大而靈活的字符串處理功能&#xff0c;能夠滿足各種復雜的文本處理需求。 1.1 QString類概述 QString是Qt中處理字符串的核心類&#xff0c;它基于Unicode編碼&#xff0c…

qt5靜態版本對應的pcre編譯

下載 https://sourceforge.net/projects/pcre/files/pcre/8.45/ 不同版本qt對應不同pcre 編譯 啟動vs2013的開發人員命令&#xff0c;可以找到cl程序 nmake環境設置到系統path中 cd C:\pcre-8.45 mkdir build_static cd build_static cmake .. -G "NMake Makefiles" …

JimuReport 積木報表 v2.1.1 版本發布,免費開源的報表和大屏

項目介紹 積木報表&#xff0c;是一款免費的數據可視化報表&#xff0c;含報表、打印、大屏和儀表盤&#xff0c;像搭建積木一樣完全在線設計&#xff01;功能涵蓋&#xff1a;復雜報表、打印設計、圖表報表、門戶設計、大屏設計等&#xff01; 分兩大模塊&#xff1a;JimuRepo…

基于python django的農業可視化系統,以奶牛牧場為例

摘 要 本文課題圍繞畜牧業高質量發展中牧場管理的現狀&#xff0c;現代牧場飼養模式上存在的數據比較零碎、飼養過程中容易經驗主義、生產產量不穩、產出效益低、奶牛體況的不合理等現狀&#xff0c;設計了多參數大數據智能牧場生產管理決策支撐體系。以牧場信息系統的建設為背…

無人機吊艙與遙控器匹配技術解析

一、 無人機吊艙如何與遙控器“對上暗號”&#xff1f;在無人機執行物資投送、電力巡檢、災害搜救等任務時&#xff0c;吊艙&#xff08;即懸掛于機身下方的任務設備&#xff09;常成為核心作業單元。但要讓遙控器“指揮”吊艙&#xff0c;兩者必須實現雙向通信協議互通、電氣接…

C#模擬pacs系統接收并解析影像設備數據(DICOM文件解析)

上篇文件介紹了什么dicomhttps://blog.csdn.net/qq_39569480/article/details/149641920?spm=1001.2014.3001.5502 本篇文章我們來使用fo_dicom接收并解析dicom文件。 文章結尾附源碼。 1.開發環境 visual studio 2019 .netframwork 4.8 2.關鍵知識點 dicom三要素為 AE t…

在 IntelliJ IDEA 中打開這個用于設置 Git 用戶名(Name)和郵箱(Email)的特定彈窗

要在 IntelliJ IDEA 中打開這個用于設置 Git 用戶名&#xff08;Name&#xff09;和郵箱&#xff08;Email&#xff09;的特定彈窗&#xff08;如下圖&#xff09;&#xff0c;可以通過以下幾種常見方法觸發&#xff1a;https://i.im.ge/2024/07/16/Kt6r1i.IDE-Git-UserName-Co…

redis 源碼閱讀

官網下載zip&#xff1a; 本文即是文件創建時間時候的版本~ 文章目錄目錄結構/srcint main()服務端 server足夠的熵值 entropyumask掩碼系統初始化*重啟機制&#xff1a;保存執行數據 以便后續重啟服務哨兵模式 sentinelrdb aof解析命令行參數聲明實現的位置目錄結構 目錄/文…