1. 程式人生 > >圖論演算法----並查集中的啟發式合併

圖論演算法----並查集中的啟發式合併

一、啟發式合併的演算法原理

一聽這名字,感覺好高大上,實際上很簡單。

由於路徑壓縮在有些題目會損失海量的資訊,用暴力並查集又要超時,所以就出現了啟發式合併演算法。

之前講過並查集的大部分時間都浪費在了find()函式上,於是就對find()函式進行了優化,其實啟發式合併演算法可以使find()函式的時間複雜度控制在O(logn)左右。

並查集是一種樹型的資料結構,而樹也有它的深度,如果我們把一棵深度大的樹的根節點接在了一棵深度小的樹上,那麼整棵樹的深度為那一棵深度大的樹的深度+1,如果我們把一棵深度小的樹的根節點接在了一棵深度大的樹上,則整棵樹的深度為max(深度小的樹的深度+1,深度大的樹的深度)。這就是啟發式合併的原理。

如果感覺很難理解,看下面的圖就知道了。

1、有兩棵樹,一棵高度為3,一棵高度為5。


2、如果是普通的合併,就會造成高度為6的樹。


3、如果是啟發式合併,最後的樹的的高度為5。


好了,啟發式合併的原理講得差不多了,可以發程式碼了,記住height[]陣列初始化為1。

二、啟發式合併的程式碼

void qfsunion(int x,int y)
{
    int a=find(x);
    int b=find(y);
    if(height[a]>height[b])
        fa[b]=a;
    else if(height[a]<height[b])
        fa[a]=b;
    else{
        fa[a]=b;
        height[b]++;
    }
}
配套一個find()函式:
int find(x)
{
     while(fa[x]!=0)
         x=fa[x];
      return x;
}

三、啟發式合併與路徑壓縮之間的問題

這時有人會問了:為什麼find()函式不用路徑壓縮呢?

原因很簡單,因為有了路徑壓縮,啟發式合併演算法就沒有保護資料的效果了。

可以看出,啟發式合併+路徑壓縮並不是最好的選擇,而且路徑壓縮在主動改變樹的高度,但是height陣列的值不能同步改變,有可能讓啟發式合併出錯。

相關推薦

演算法----集中啟發式合併

一、啟發式合併的演算法原理 一聽這名字,感覺好高大上,實際上很簡單。 由於路徑壓縮在有些題目會損失海量的資訊,用暴力並查集又要超時,所以就出現了啟發式合併演算法。 之前講過並查集的大部分時間都浪費在了

演算法----集中的路徑壓縮

一、演算法知識 並查集是一種樹型的高階資料結構,用於處理集合的合併和查詢的問題,應用十分廣泛。 因為主要用合併和查詢,所以叫做並查集。但是要注意,這裡的集合是不能相交的。 並查集主要有兩個函式:find(a)和union(a,b)。 find(a)主要用於查詢a所在樹的根節

演算法----集父親查詢寫法比較 DisjointSet

1. 花式查詢並查集               class DisjointSet: def __init__(self, n): # Args: #

簡要題解--搜尋-集-dp-樹形-拓撲-tarjan等等

attentions:對我而言非常好的一道題!最長路! 有幾個點 1、這道題轉化成最長路來求解,方法和最短路類似 2、但這道題是點有正權且只有負權邊,且路徑為單向!那麼精妙之處在於,可以將點權轉化為邊權!!! 3、由於題目中可能出現正環(和最短路相反

集中合併、刪除操作

思路:在一般的並查集操作中設立虛父親節點,當刪除x的時候,不是真的刪除x,而是通過一個對映,即令tmp[x] = cnt, parent[cnt] = cnt;這樣x就從原來的集合中獨立出來了,而我們每次合併x,y的時候,只需合併tmp[x], tmp[y]就可以了。

P1892-團伙【集】

正題 大意 兩個人如果認識就只有兩種關係,敵人或朋友 而: 朋友的朋友是朋友 敵人的敵人是朋友 (敵人之間也可能是敵人) 求團伙總數 解題思路 就像做食物鏈一樣,如

集中啟發式合併

演算法原理 並查集一般有兩種方法來保持複雜度不退化,一種是路徑壓縮,另一種則是按照秩來做啟發式合併。 一般情況下我們都是用第一種,壓縮路徑通過遞推找到祖先節點後,在回溯時將它的子孫節點都直接指向祖先,這樣以後每次呼叫Find( )函式找父親時複雜度就變成了O(1)。但是路

Agri-Net的Kruskal演算法+集實現(按大小合併+路徑壓縮)

Agri-Net的Kruskal演算法+並查集實現 演算法複雜度分析     對所有的邊進行排序,排序複雜度為O(mlogm),隨後對邊進行合併,合併使用並查集,並查集使用link by size的方式實現,同時在find函式實現了路徑壓縮。每

演算法-集-加邊無向

題目描述:給你一個 n 個點,m 條邊的無向圖,求至少要在這個的基礎上加多少條無向邊使得任意兩個點可達~ 輸入描述:第一行兩個正整數 n 和 m 。 接下來的m行中,每行兩個正整數 i 、 j ,表示點i與點j之間有一條無向道路。輸出描述:輸出一個整數,表示答案例 :輸入4

簡單合併集中的子集樹

//此程式碼是資料結構的原始模板,可以剛接觸或考研時借鑑下,不適於刷題 #include<stdio.h> #include<malloc.h> #define ERROR 0

NOIP複賽複習(十三)演算法鞏固與提高

一、圖的儲存   1、鄰接矩陣   假設有n個節點,建立一個n×n的矩陣,第i號節點能到達第j號節點就將[i][j]標記為1(有權值標記為權值),  樣例如下圖:   /*無向圖,無權值*/ i

資料結構和演算法:第八章 演算法

9.1 若干定義 圖的定義:一個圖(Graph) G=(V,E)是由頂點的集合V和邊Edge的集合E組成的。每一條邊就是一個頂點對(v,w),其中(v,w) ∈E。有時候也把邊叫做弧。如果頂點對是有序的,那麼圖就是有向的。有的圖也叫做有向圖。頂點w和頂點v鄰接當且僅當(v,w)

演算法模板

Under the bridge downtown Forgot about my love Under the bridge downtown I gave my life away Luogu P4779【模板】單源最短路徑(標準版) //時間複雜度O((n+m)log

PAT 備考第一天——演算法(一)

大綱: 必考考點: 1.圖的定義和相關術語 2.圖的儲存(鄰接矩陣和鄰接表) 3.圖的遍歷(DFS和BFD) 4.最短路徑演算法 5.拓撲排序 非重點考點: 1.關鍵路徑 2.最短路徑中的Bellman-Ford和SPFA 甲級考綱以外的考點: 最小生成樹演算法 一、圖的

第9章 演算法

圖的表示方法 鄰接矩陣 : adjacent matrix是一個二位陣列,對於每條邊(u,v),置A[u][v]等於true;否則,陣列的元素就是false。如果邊有一個權,那麼可以置A[u][v]等於該權,而使用一個很大或者很小的權作為標記表示不存在的邊。適用於稠密的圖

牛客練習賽32 D Tarjan無向求橋+集維護

題目描述: 小p和他的朋友約定好去遊樂場遊玩,但是他們到了遊樂場後卻互相找不到對方了。 遊樂場可以看做是一張n個點,m條道路的圖,每條道路有邊權wi,表示第一次經過該道路時的花費(第二次及以後經過時花費為0)。 現在,小p要去找他的朋友,但他的朋友行蹤很詭異,小p總是要遍歷完這n個點才能找到他,

演算法講解--最短路--Dijkstra演算法

一.緒論 要學習最短路演算法我們首先應該知道什麼是圖以及什麼是最短路。 圖在離散數學中的定義為:圖G=(V,E)是一個二元組(V,E)使得E⊆[V]的平方,所以E的元素是V的2-元子集。為了避免符號上的混淆,我們總是預設V∩B=Ø。集合V中的元素稱為圖G的定

[C++一本通-演算法] 例4-4 最小花費

題目描述 在n個人中,某些人的銀行賬號之間可以互相轉賬。這些人之間轉賬的手續費各不相同。給定這些人之間轉賬時需要從轉賬金額里扣除百分之幾的手續費,請問A最少需要多少錢使得轉賬後B收到100元。 輸入輸出格式 輸入格式: 第一行輸入兩個正整數n,m,分別表示總人數和可以互相轉賬的人的對數。 以下m行每行輸入三個

模板庫(三) - 演算法模板

寫在前面 “模板庫”這一系列文章用來複習 O I OI

演算法進階習題集

最近在做一些圖論的題,像steiner 樹一類的演算法,還有網路流,下面轉載了500道進階練習題,希望以後能針對性的訓練一下,畢竟是熟能生巧,不練不知道。 =============================以下是最小生成樹+並查集============