主頁 > 軟體設計 > 并查集入門(持續更新!)

并查集入門(持續更新!)

2021-03-03 16:57:55 軟體設計

嘿大家,我又回來了,今天我們來介紹一下并查集,它是一種很高效的演算法,值得學習

文章目錄

  • 引入
  • 一、思考
  • 二、具體實作
  • 三、拓展題
  • 拓展題AC代碼
  • 思路講解
  • 總結


現在,我們先看看一道題,簡單思考一下,

引入

洛谷P3367

如題,現在有一個并查集,你需要完成合并和查詢操作,

輸入輸出格式

輸入格式:
第一行包含兩個整數N、M,表示共有N個元素和M個操作,

接下來M行,每行包含三個整數Zi、Xi、Yi

當Zi=1時,將Xi與Yi所在的集合合并

當Zi=2時,輸出Xi與Yi是否在同一集合內,是的話輸出Y;否則話輸出N

輸出格式:
如上,對于每一個Zi=2的操作,都有一行輸出,每行包含一個大寫字母,為Y或者N

輸入輸出樣例

輸入樣例#1:
4 7
2 1 2
1 1 2
2 1 2
1 3 4
2 1 4
1 2 3
2 1 4
輸出樣例#1:
N
Y
N
Y
說明

時空限制:1000ms,128M

資料規模:

對于30%的資料,N<=10,M<=20;

對于70%的資料,N<=100,M<=1000;

對于100%的資料,N<=10000,M<=200000,


一、思考

看完題目,你可能一頭霧水,也可能靈光乍現,我們就先來了解一下什么是并查集:

并查集被很多OIer認為是最簡潔而優雅的資料結構之一,主要用于解決一些元素分組的問題,它管理一系列不相交的集合,并支持兩種操作:

合并(Union):把兩個不相交的集合合并為一個集合,
查詢(Find):查詢兩個元素是否在同一個集合中,

是的,我們可以把并查集理解成是一種集合資料庫,里面記錄了集合之間的關系,這樣說未免讓人摸不著頭,我們就先來引入一個實際問題

現在有5個人,名叫1,2,3,4,5,他們有各自的老大(老大可以是自己),現在規定,1,2,3共用一個老大,4,5共用一個老大(或者說是共用一個組),我們可以如何表示呢?

是的,我們當然可以把1,2,3存入一個陣列叫a,把4,5存入一個陣列叫b,但這樣未免慢了些,有什么快捷的方法嗎?有的,并查集

我們先設定一個陣列名叫p,如果要實作用一條陣列存盤這樣的關系,我們可以規定1,2,3的老大是1,4和5的老大是4,那么如果將p的下標定義為人員,內部存的資料定為它的老大,是不是就可以得到這個關系了呢?沒錯,得到的p就是[1,1,1,4,4](下標從1到5),

我們執行查詢的時候,比如說我要知道3的老大是誰,我只需要呼叫p[3],里面存的就是集合頭子,

好的,現在我們已經介紹了查詢了,如何合并呢?聰明的你一定想到了,如果我要合并上述第一個和第二個集合,我只需要把第二個集合的內容都改成第一個的頭子即可,

二、具體實作

這里find()函式我們要單獨拎出來講講:

int f[10005];
int find(int k){
    return f[k]==k?k:f[k] = find(f[k]);
}

我們注意到find是這樣運作的:
1.如果找到的f陣列內容就是本身查詢的k值(自己就是自己的頭子,相當于上述例子p[1]=1),則直接回傳k值
2.如果不是,就要去更深層挖掘,也就是我們寫的遞回程式,令f[k] = find(f[k]),一方面在尋找k的總頭子(頭子也可能有頭子),一方面也直接把找到的值賦給了f[k],我們管這叫路徑壓縮
(路徑壓縮優化后,并查集的時間復雜度已經比較低了,絕大多數不相交集合的合并查詢問題都能夠解決)

代碼如下(示例):

#include<bits/stdc++.h>
using namespace std;
int f[10005];
int find(int k){
    if(f[k]==k)return k;
    else return f[k] = find(f[k]);
    
}
int main(){
     int n,m;
    cin >> n >> m;
    for(int i=1;i<=n;i++)f[i] = i;
    int type,a,b;
    while(m--)
    {
        scanf("%d%d%d",&type,&a,&b);
        if(type==1){
            f[find(b)] = find(a);
        }else
        {
                if(find(b)==find(a))
                cout << "Y" << endl;
            else
                cout << "N" << endl;
        }
        
    }
    
    
    
    return 0;
}

三、拓展題

ZCMU-1435
盟國
Time Limit: 3 Sec Memory Limit: 128 MB
Submit: 592 Solved: 143
世界上存在著N個國家,簡單起見,編號從0~N-1,假如a國和b國是盟國,b國和c國是盟國,那么a國和c國也是盟國,另外每個國家都有權宣布退盟(注意,退盟后還可以再結盟),

定義下面兩個操作:

“M X Y” :X國和Y國結盟 (如果X與Z結盟,Y與Z結盟,那么X與Y也自動結盟).

“S X” :X國宣布退盟 (如果X與Z結盟,Y與Z結盟,Z退盟,那么X與Y還是聯盟).

Input
多組case,

每組case輸入一個N和M (1 ≤ N ≤ 100000 , 1 ≤ M ≤ 1000000),N是國家數,M是運算元,

接下來輸入M行操作

當N=0,M=0時,結束輸入

Output
對每組case輸出最終有多少個聯盟(如果一個國家不與任何國家聯盟,它也算一個獨立的聯盟),格式見樣例,

Sample Input

5 6
M 0 1
M 1 2
M 1 3
S 1
M 1 2
S 3
3 1
M 1 2
0 0

Sample Output

Case #1: 3
Case #2: 2

拓展題AC代碼

在這里我就先上代碼了,有能力的同學可以先看,看不懂的話下面我會解釋

#include<bits/stdc++.h>
using namespace std;
const int maxn = 2e6+5;
int f[maxn],ff[maxn],vis[10000];
int find(int k){
    if(f[k]==k)return k;
    else return f[k] = find(f[k]);

}
int main(){
       int m,n,ca = 1;
    while(~scanf("%d%d",&n,&m))
    {
        int pos = n;
        if(m==0&&n==0)break;
        for(int i=0;i<maxn;i++)f[i] = ff[i] = i;
        while(m--){
            char str[2];
            int a,b;
            scanf("%s %d",str,&a);
            if(str[0]=='M'){
                scanf("%d",&b);
                int fa = find(ff[a]);
                int fb = find(ff[b]);
                if(fa!=fb)f[fa] = fb;
             }else if(str[0]=='S')
             {
                 ff[a] = pos;
                 f[pos] = pos;
                 pos++;
             }
        }
        set<int>s;
        for(int i=0;i<n;i++)
        s.insert(find(ff[i]));
        
        printf("Case #%d: %d\n",ca++,s.size());
        
    }
    return 0;
    }

思路講解

在這里插入圖片描述

我們可以看到,現在這是一個關于數集合的樹,可以得到一個關系,2,3,4的老大是1,5和6的老大是4,如果我們現在要實作洗掉的操作,我們該怎么做呢?

洗掉操作比合并操作稍微復雜一點,
首先,我們已經用f陣列保存了原來結點間的關系(2,3,4的老大是1,5和6的老大是4),現在我們想要讓4從這個關系圖中剔除,我們就需要新建一個陣列ff[],里面存盤的就是最新的關系

也就是說,洗掉操作要這樣執行

 ff[a] = pos;
 f[pos] = pos;
 pos++;
 //pos就是從n開始增大,因為題目資料范圍是0 to n-1

我們首先讓ff內的a指向pos,(pos已經在原有資料范圍之外了),我們可以理解為a這個資料被流放了,同時在f陣列執行f[pos] = pos,使得讓pos的老大就是本身,這樣在find運行時可以作為一個獨立的個體

我們用一個形象的比喻:
澳大利亞是英國殖民時期的罪犯流放地,但剛開始并沒有罪犯對吧,現在,英國本來有6個小混混,他們之間的關系如上圖,現在4號小混混(小頭子)放下了殺人罪,被法律規定要流放到澳大利亞,成為那里第一個犯人,于是,執行官(也就是ff陣列把ff[4]=7標記了)把4送到了7這個地方流放了,也就是新開辟的澳大利亞區域,但是,盡管4號小頭子被流放了,它兩個手下5和6的總頭子仍然是1,為了以后警察能通過f陣列仍然找到最大的頭子1,f陣列原來資料范圍內的資料是不會有變化的,只是會添加一條 f[pos] = pos,意味著如果澳大利亞警察找4的頭子,那還是會找到4本身的,

接下來講講稍微簡單的合并操作

int fa = find(ff[a]);
int fb = find(ff[b]);
if(fa!=fb)f[fa] = fb;

我們可以注意到,fa是在ff陣列(最新的犯罪記錄,用了上面的例子了)里用find函式查找a的頭子(注意:find函式用的還是f陣列,原因上面已經講過了),fb同理,如果頭子相同,那么不必執行操作,如果不同,就要在f陣列里面記錄a的頭子就是b的頭子(其實反過來也是對的)

總結

今天介紹了了并查集的簡單入門,查詢,合并和刪改,并做到了一些應用,后面那個例子講的可能不是非常恰當,但希望能幫助你理解,
如有出錯,希望在評論區告知,希望能給你們派上用場,

轉載請註明出處,本文鏈接:https://www.uj5u.com/ruanti/265446.html

標籤:其他

上一篇:用VSCode終端實作重定向比較程式輸出和正確輸出

下一篇:【天工Godwork精品教程】任務二:匯入控制點、POS權重設定、連接點分布檢查、自由空三

標籤雲
其他(157675) Python(38076) JavaScript(25376) Java(17977) C(15215) 區塊鏈(8255) C#(7972) AI(7469) 爪哇(7425) MySQL(7132) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5869) 数组(5741) R(5409) Linux(5327) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4554) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2429) ASP.NET(2402) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) 功能(1967) .NET技术(1958) Web開發(1951) python-3.x(1918) HtmlCss(1915) 弹簧靴(1913) C++(1909) xml(1889) PostgreSQL(1872) .NETCore(1853) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • 面試突擊第一季,第二季,第三季

    第一季必考 https://www.bilibili.com/video/BV1FE411y79Y?from=search&seid=15921726601957489746 第二季分布式 https://www.bilibili.com/video/BV13f4y127ee/?spm_id_fro ......

    uj5u.com 2020-09-10 05:35:24 more
  • 第三單元作業總結

    1.前言 這應該是本學期最后一次寫作業總結了吧。總體來說,對作業的節奏也差不多掌握了,作業做起來的效率也更高了。雖然和之前的作業一樣,作業中都要用到新的知識,但是相比之前,更加懂得了如何利用工具以及資料。雖然之間卡過殼,但總體而言,這幾次作業還算完成的比較好。 2.作業程序總結 相比前兩個單元,此單 ......

    uj5u.com 2020-09-10 05:35:41 more
  • 北航OO(2020)第四單元博客作業暨課程總結博客

    北航OO(2020)第四單元博客作業暨課程總結博客 本單元作業的架構設計 在本單元中,由于UML圖具有比較清晰的樹形結構,因此我對其中需要進行查詢操作的元素進行了包裝,在樹的父節點中存盤所有孩子的參考。考慮到性能問題,我采用了快取機制,一次查詢后盡可能快取已經遍歷過的資訊,以減少遍歷次數。 本單元我 ......

    uj5u.com 2020-09-10 05:35:48 more
  • BUAA_OO_第四單元

    一、UML決議器設計 ? 先看下題目:第四單元實作一個基于JDK 8帶有效性檢查的UML(Unified Modeling Language)類圖,順序圖,狀態圖分析器 MyUmlInteraction,實際上我們要建立一個有向圖模型,UML中的物件(元素)可能與同級元素連接,也可與低級元素相連形成 ......

    uj5u.com 2020-09-10 05:35:54 more
  • 6.1邏輯運算子

    邏輯運算子 1. && 短路與 運算式1 && 運算式2 01.運算式1為true并且運算式2也為true 整體回傳為true 02.運算式1為false,將不會執行運算式2 整體回傳為false 03.只要有一個運算式為false 整體回傳為false 2. || 短路或 運算式1 || 運算式2 ......

    uj5u.com 2020-09-10 05:35:56 more
  • BUAAOO 第四單元 & 課程總結

    1. 第四單元:StarUml檔案決議 本單元采用了圖模型決議UML。 UML檔案可以抽象為圖、子圖、邊的邏輯結構。 在實作中,圖的節點包括類、介面、屬性,子圖包括狀態圖、順序圖等。 采用了三次遍歷UML元素的方法建圖,第一遍遍歷建點,第二、三次遍歷設定屬性、連邊,實作圖物件的初始化。這里借鑒了一些 ......

    uj5u.com 2020-09-10 05:36:06 more
  • 談談我對C# 多型的理解

    面向物件三要素:封裝、繼承、多型。 封裝和繼承,這兩個比較好理解,但要理解多型的話,可就稍微有點難度了。今天,我們就來講講多型的理解。 我們應該經常會看到面試題目:請談談對多型的理解。 其實呢,多型非常簡單,就一句話:呼叫同一種方法產生了不同的結果。 具體實作方式有三種。 一、多載 多載很簡單。 p ......

    uj5u.com 2020-09-10 05:36:09 more
  • Python 資料驅動工具:DDT

    背景 python 的unittest 沒有自帶資料驅動功能。 所以如果使用unittest,同時又想使用資料驅動,那么就可以使用DDT來完成。 DDT是 “Data-Driven Tests”的縮寫。 資料:http://ddt.readthedocs.io/en/latest/ 使用方法 dd. ......

    uj5u.com 2020-09-10 05:36:13 more
  • Python里面的xlrd模塊詳解

    那我就一下面積個問題對xlrd模塊進行學習一下: 1.什么是xlrd模塊? 2.為什么使用xlrd模塊? 3.怎樣使用xlrd模塊? 1.什么是xlrd模塊? ?python操作excel主要用到xlrd和xlwt這兩個庫,即xlrd是讀excel,xlwt是寫excel的庫。 今天就先來說一下xl ......

    uj5u.com 2020-09-10 05:36:28 more
  • 當我們創建HashMap時,底層到底做了什么?

    jdk1.7中的底層實作程序(底層基于陣列+鏈表) 在我們new HashMap()時,底層創建了默認長度為16的一維陣列Entry[ ] table。當我們呼叫map.put(key1,value1)方法向HashMap里添加資料的時候: 首先,呼叫key1所在類的hashCode()計算key1 ......

    uj5u.com 2020-09-10 05:36:38 more
最新发布
  • 【中介者設計模式詳解】C/Java/JS/Go/Python/TS不同語言實作

    * 中介者模式是一種行為型設計模式,它可以用來減少類之間的直接依賴關系,
    * 將物件之間的通信封裝到一個中介者物件中,從而使得各個物件之間的關系更加松散。
    * 在中介者模式中,物件之間不再直接相互互動,而是通過中介者來中轉訊息。 ......

    uj5u.com 2023-04-20 08:20:47 more
  • 露天煤礦現場調研和交流案例分享

    他們集團的資訊化公司及研究院在一個礦區正在做智能礦山的統一平臺的 試點,專案投資大概1億,包括了礦山的各方面的內容,顯示得我們這次交流有點多余。他們2年前開始做智能礦山的規劃,有很多煤礦行業專家的加持,他們的描述是非常完美,但是去年底應該上線的平臺,現在還沒有看到影子。他們確實有很多場景需求,但是被... ......

    uj5u.com 2023-04-20 08:20:25 more
  • 《社區人員管理》實戰案例設計&個人案例分享

    設計是一個讓人夢想成真程序,開始編碼、測驗、除錯之前進行需求分析和架構設計,才能保證關鍵方面都做正確 ......

    uj5u.com 2023-04-20 08:20:17 more
  • 軟體架構生態化-多角色交付的探索實踐

    作為一個技術架構師,不僅僅要緊跟行業技術趨勢,還要結合研發團隊現狀及痛點,探索新的交付方案。在日常中,你是否遇到如下問題 “ 業務需求排期長研發是瓶頸;非研發角色感受不到研發技改提效的變化;引入ISV 團隊又擔心質量和安全,培訓周期長“等等,基于此我們探索了一種新的技術體系及交付方案來解決如上問題。 ......

    uj5u.com 2023-04-20 08:20:10 more
  • 【中介者設計模式詳解】C/Java/JS/Go/Python/TS不同語言實作

    * 中介者模式是一種行為型設計模式,它可以用來減少類之間的直接依賴關系,
    * 將物件之間的通信封裝到一個中介者物件中,從而使得各個物件之間的關系更加松散。
    * 在中介者模式中,物件之間不再直接相互互動,而是通過中介者來中轉訊息。 ......

    uj5u.com 2023-04-20 08:19:44 more
  • 露天煤礦現場調研和交流案例分享

    他們集團的資訊化公司及研究院在一個礦區正在做智能礦山的統一平臺的 試點,專案投資大概1億,包括了礦山的各方面的內容,顯示得我們這次交流有點多余。他們2年前開始做智能礦山的規劃,有很多煤礦行業專家的加持,他們的描述是非常完美,但是去年底應該上線的平臺,現在還沒有看到影子。他們確實有很多場景需求,但是被... ......

    uj5u.com 2023-04-20 08:19:07 more
  • 《社區人員管理》實戰案例設計&個人案例分享

    設計是一個讓人夢想成真程序,開始編碼、測驗、除錯之前進行需求分析和架構設計,才能保證關鍵方面都做正確 ......

    uj5u.com 2023-04-20 08:18:57 more
  • 軟體架構生態化-多角色交付的探索實踐

    作為一個技術架構師,不僅僅要緊跟行業技術趨勢,還要結合研發團隊現狀及痛點,探索新的交付方案。在日常中,你是否遇到如下問題 “ 業務需求排期長研發是瓶頸;非研發角色感受不到研發技改提效的變化;引入ISV 團隊又擔心質量和安全,培訓周期長“等等,基于此我們探索了一種新的技術體系及交付方案來解決如上問題。 ......

    uj5u.com 2023-04-20 08:18:49 more
  • 05單件模式

    #經典的單件模式 public class Singleton { private static Singleton uniqueInstance; //一個靜態變數持有Singleton類的唯一實體。 // 其他有用的實體變數寫在這里 //構造器宣告為私有,只有Singleton可以實體化這個類! ......

    uj5u.com 2023-04-19 08:42:51 more
  • 【架構與設計】常見微服務分層架構的區別和落地實踐

    軟體工程的方方面面都遵循一個最基本的道理:沒有銀彈,架構分層模型更是如此,每一種都有各自優缺點,所以請根據不同的業務場景,并遵循簡單、可演進這兩個重要的架構原則選擇合適的架構分層模型即可。 ......

    uj5u.com 2023-04-19 08:42:41 more