欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

C/C++并查集的查詢與合并實(shí)現(xiàn)原理

 更新時(shí)間:2023年02月13日 09:32:52   作者:Ggggggtm  
這篇文章主要介紹了C/C++并查集的查詢與合并,并查集是一種用來管理元素分組情況的數(shù)據(jù)結(jié)構(gòu)。并查集可以高效地進(jìn)行如下操作

標(biāo)題:并查集的查詢與合并詳解 作者:@Ggggggtm 寄語:與其忙著訴苦,不如低頭趕路,奮路前行,終將遇到一番好風(fēng)景

一、并查集的概念

并查集是一種樹形的數(shù)據(jù)結(jié)構(gòu)。使用樹型結(jié)構(gòu)來存儲(chǔ)數(shù)據(jù)。樹根的編號(hào)即為整個(gè)樹的標(biāo)號(hào),且每個(gè)節(jié)點(diǎn)存儲(chǔ)的數(shù)據(jù)是他的父節(jié)點(diǎn)下標(biāo)。

并查集被很多OIer認(rèn)為是最簡(jiǎn)潔而優(yōu)雅的數(shù)據(jù)結(jié)構(gòu)之一,主要用于解決一些元素分組的問題。它管理一系列不相交的集合,并支持兩種操作:

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

二、并查集的實(shí)現(xiàn)

1.并查集不同集合(樹)的形成

我們把并查集不同集合(樹)的實(shí)現(xiàn)主要分為以下幾個(gè)點(diǎn):

  • 我們先給出n個(gè)數(shù)據(jù),把n個(gè)數(shù)據(jù)存儲(chǔ)到不同的集合當(dāng)中(p[ i ] = i),在這里我們把每個(gè)p[ i ]分別看成一個(gè)不同集合(也就是一棵樹)。
  • p[ i ] = i,i即為這棵樹的編號(hào),這顆樹下面的孩子節(jié)點(diǎn)存儲(chǔ)的數(shù)據(jù)是父節(jié)點(diǎn)的下標(biāo)。
  • 當(dāng)p[ i]=i 時(shí),就相當(dāng)于找到了根節(jié)點(diǎn)。
  • 我們剛開始每個(gè)集合中的元素只有一個(gè)。后續(xù)合并后,集合元素個(gè)數(shù)不斷增加。

2.find()函數(shù)找一個(gè)元素集合的編號(hào)

(元素所屬于樹的祖宗)

我們查找一個(gè)元素的集合,把元素的當(dāng)作下標(biāo)傳給find()函數(shù),代碼如下:

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

我們p[x]中存儲(chǔ)的正是他的父節(jié)點(diǎn),從而就可以一直往上查找,直到p[ x ]=x時(shí)結(jié)束。當(dāng)p[ x ]=x時(shí),就相當(dāng)于找到了根節(jié)點(diǎn)。此時(shí)的p[ x ]存儲(chǔ)的是這棵樹的編號(hào)。我這發(fā)現(xiàn),剛開始每個(gè)集合當(dāng)中都只有一個(gè)元素,也就是p[ x ],后面我們會(huì)對(duì)不同的集合進(jìn)行合并,使得一個(gè)集合有多個(gè)元素。

我們?cè)僬易孀诠?jié)點(diǎn)時(shí)進(jìn)行了路徑壓縮。什么是路徑壓縮呢?路徑壓縮就是我們?cè)诓檎夷硞€(gè)元素的祖宗時(shí),在找父節(jié)點(diǎn)的這條路經(jīng)上的元素都指向祖宗節(jié)點(diǎn),以便于我們后面的查找的時(shí)間復(fù)雜度近乎O(1)。

3.合并兩個(gè)不同集合(合并兩棵不同的樹)

我們直到了每棵樹的根節(jié)點(diǎn)存儲(chǔ)的是這個(gè)樹的編號(hào),而不是父節(jié)點(diǎn)。當(dāng)我們要合并兩顆樹時(shí),我們只需要把一棵樹的根節(jié)點(diǎn)存儲(chǔ)的編號(hào)改為另一棵樹的根節(jié)點(diǎn)編號(hào)。簡(jiǎn)單的理解就是一個(gè)樹的根節(jié)不再是根節(jié)點(diǎn),而是一個(gè)子節(jié)點(diǎn),該樹的根節(jié)點(diǎn)存儲(chǔ)的也不再是編號(hào),而是存儲(chǔ)的父節(jié)點(diǎn),該父節(jié)點(diǎn)就是另一棵樹的根節(jié)點(diǎn)。我們看代碼:

//合并  把a(bǔ)的祖宗節(jié)點(diǎn)的父節(jié)點(diǎn)當(dāng)作b的祖宗結(jié)點(diǎn)
p[find(a)]=find(b);

4.查詢兩個(gè)元素是否在一個(gè)集合

我們有了find()函數(shù),就可以很簡(jiǎn)單的判斷出兩個(gè)元素的是否在同一個(gè)集合當(dāng)中(兩個(gè)元素是否在同一個(gè)樹中)。我們只需要判斷兩個(gè)元素集合的編號(hào)是否相同(兩個(gè)元素的祖宗節(jié)點(diǎn)是否相同)即可。我們看代碼:

//看a、b兩個(gè)元素是否在同一個(gè)集合當(dāng)中
if(find(a)==find(b))
    cout<<"Yes"<<endl;
else
    cout<<"No"<<endl;

5.并查集例題訓(xùn)練1

一共有 n 個(gè)數(shù),編號(hào)是1∼n,最開始每個(gè)數(shù)各自在一個(gè)集合中。

現(xiàn)在要進(jìn)行m個(gè)操作,操作共有兩種:

  • M a b,將編號(hào)為a和b的兩個(gè)數(shù)所在的集合合并,如果兩個(gè)數(shù)已經(jīng)在同一個(gè)集合中,則忽略這個(gè)操作;
  • Q a b,詢問編號(hào)為a和b的兩個(gè)數(shù)是否在同一個(gè)集合中;

輸入格式:

第一行輸入整數(shù)n和m。

接下來m行,每行包含一個(gè)操作指令,指令為M a bQ a b中的一種。

輸出格式:

對(duì)于每個(gè)詢問指令Q a b,都要輸出一個(gè)結(jié)果,如果a和b在同一集合內(nèi),則輸出Yes,否則輸出No。

每個(gè)結(jié)果占一行。

數(shù)據(jù)范圍:

1≤n,m≤10e5 1≤n,m≤10e5

輸入樣例:

4 5
M 1 2
M 3 4
Q 1 2
Q 1 3
Q 3 4

輸出樣例:

Yes
No
Yes

答案如下:

#include<iostream>
using namespace std;
const int N=100010;
int p[N];
//找祖宗+路徑壓縮
int find(int x)
{
    if(p[x]!=x)
    {
        p[x]=find(p[x]);
    }
    return p[x];
}
int main()
{
    int n,m;
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++)
        p[i]=i;
    while(m--)
    {
        char op[2];
        int a,b;
        cin>>op>>a>>b;
        if(op[0]=='M')
        {
            //合并  把a(bǔ)的祖宗節(jié)點(diǎn)的父節(jié)點(diǎn)當(dāng)作b的祖宗結(jié)點(diǎn)
            p[find(a)]=find(b);
        }
        else
        {
            if(find(a)==find(b))
                cout<<"Yes"<<endl;
            else
                cout<<"No"<<endl;
        }
    }
    return 0;
}

6.并查集例題訓(xùn)練2

給定一個(gè)包含nn個(gè)點(diǎn)(編號(hào)為1∼n1∼n)的無向圖,初始時(shí)圖中沒有邊。

現(xiàn)在要進(jìn)行mm個(gè)操作,操作共有三種:

  • C a b,在點(diǎn)aa和點(diǎn)bb之間連一條邊,aa和bb可能相等;
  • Q1 a b,詢問點(diǎn)aa和點(diǎn)bb是否在同一個(gè)連通塊中,aa和bb可能相等;
  • Q2 a,詢問點(diǎn)aa所在連通塊中點(diǎn)的數(shù)量;

輸入格式

第一行輸入整數(shù)nn和mm。

接下來mm行,每行包含一個(gè)操作指令,指令為C a b,Q1 a bQ2 a中的一種。

輸出格式

對(duì)于每個(gè)詢問指令Q1 a b,如果aa和bb在同一個(gè)連通塊中,則輸出Yes,否則輸出No。

對(duì)于每個(gè)詢問指令Q2 a,輸出一個(gè)整數(shù)表示點(diǎn)aa所在連通塊中點(diǎn)的數(shù)量

每個(gè)結(jié)果占一行。

數(shù)據(jù)范圍

1≤n,m≤1051≤n,m≤105

輸入樣例:

5 5
C 1 2
Q1 1 2
Q2 1
C 2 5
Q2 5

輸出樣例:

Yes
2
3

我們這個(gè)題相對(duì)于上個(gè)題就是對(duì)出了一個(gè)統(tǒng)計(jì)一個(gè)集合元素的個(gè)數(shù)。整體思路大同小異,我們直接看代碼解析:

#include<iostream>
using namespace std;
const int N=100010;
int p[N],cnt[N];
int find(int x)
{
    if(p[x]!=x)
    {
        p[x]=find(p[x]);
    }
    return p[x];
}
int main()
{
    int n,m;
    cin>>n>>m;
    for(int i=0;i<n;i++)
    {
        p[i]=i;
        cnt[i]=1;
    }
    while(m--)
    {
        char op[5];
        int a,b;
        scanf("%s",op);
        if(op[0]=='C')
        {
            scanf("%d%d",&a,&b);
            if(find(a)==find(b))
                continue;
            cnt[find(b)]+=cnt[find(a)];
            p[find(a)]=find(b);
        }
        else if(op[1]=='1')
        {
            scanf("%d%d",&a,&b);
            if(find(a)==find(b))
                printf("Yes\n");
            else
                printf("No\n");
        }
        else
        {
            scanf("%d",&a);
            printf("%d\n",cnt[find(a)]);
        }
    }
    return 0;
}

注意,我們剛開始每個(gè)集合中的元素只有一個(gè)。后續(xù)合并后,集合元素個(gè)數(shù)不斷增加。

三、總結(jié)

我們主要掌握find()函數(shù),并查集算法中,最為核心的就是find()函數(shù)。在這個(gè)算法中,路徑壓縮給我們的算法效率提高了很多,這個(gè)也是需要理解的。合并、查詢是并查集的兩個(gè)主要操作,我們也應(yīng)該熟悉理解。

到此這篇關(guān)于C/C++并查集的查詢與合并實(shí)現(xiàn)原理的文章就介紹到這了,更多相關(guān)C++并查集內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • MFC實(shí)現(xiàn)對(duì)話框編輯控件上拖拽文件

    MFC實(shí)現(xiàn)對(duì)話框編輯控件上拖拽文件

    這篇文章主要為大家詳細(xì)介紹了MFC實(shí)現(xiàn)對(duì)話框編輯控件上拖拽文件,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2020-06-06
  • C++解析obj模型文件方法介紹

    C++解析obj模型文件方法介紹

    由于本人打算使用Assimp來加載模型,這里記錄一下tinyobjloader庫的使用。之前也研究過fbxsdk,除了骨骼動(dòng)畫暫未讀取外,代碼自認(rèn)為還算可靠
    2022-09-09
  • c++ KMP字符串匹配算法

    c++ KMP字符串匹配算法

    大家好,本篇文章主要講的是c++ KMP字符串匹配算法,感興趣的同學(xué)趕快來看一看吧,對(duì)你有幫助的話記得收藏一下
    2022-01-01
  • C語言實(shí)現(xiàn)磁盤映射

    C語言實(shí)現(xiàn)磁盤映射

    磁盤映射技術(shù)通過將文件映射到內(nèi)存中,提高了文件操作的效率,本文就來介紹一下C語言實(shí)現(xiàn)磁盤映射,感興趣的可以了解一下
    2024-09-09
  • 代碼分析c++中string類

    代碼分析c++中string類

    本篇內(nèi)容通過詳細(xì)的源代碼詳細(xì)分析了C++中string類的用法以及知識(shí)點(diǎn),有興趣的讀者們參考下。
    2018-03-03
  • C語言實(shí)現(xiàn)萬年歷小程序

    C語言實(shí)現(xiàn)萬年歷小程序

    這篇文章主要為大家詳細(xì)介紹了C語言實(shí)現(xiàn)萬年歷小程序,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2019-10-10
  • C語言基礎(chǔ)指針詳解教程

    C語言基礎(chǔ)指針詳解教程

    此處對(duì)于指針做一些簡(jiǎn)要的介紹,作者實(shí)屬初學(xué),寫博客也是作者學(xué)習(xí)的一個(gè)過程,難免文章中有內(nèi)容理解不到位或者有不當(dāng)之處,還請(qǐng)朋友們不吝指正,希望大家給予支持
    2021-11-11
  • C++類和對(duì)象實(shí)例解析(二)

    C++類和對(duì)象實(shí)例解析(二)

    這篇文章主要介紹了C++類和對(duì)象,從定義出發(fā)再到實(shí)例解析,深入的理解C++類和對(duì)象,需要的朋友可以參考下
    2015-08-08
  • win10環(huán)境下C++ vs2015編譯opencv249的教程

    win10環(huán)境下C++ vs2015編譯opencv249的教程

    這篇文章主要介紹了win10環(huán)境下C++ vs2015編譯opencv249的教程,本文分步驟給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2020-03-03
  • C++使用CriticalSection實(shí)現(xiàn)線程同步實(shí)例

    C++使用CriticalSection實(shí)現(xiàn)線程同步實(shí)例

    這篇文章主要介紹了C++使用CriticalSection實(shí)現(xiàn)線程同步實(shí)例,是使用CriticalSection對(duì)前文實(shí)例的擴(kuò)展,具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2014-10-10

最新評(píng)論