1. 程式人生 > >演算法入門---java語言實現的並查集(Union-Find)小結

演算法入門---java語言實現的並查集(Union-Find)小結

圖片來自慕課網,僅僅為了記錄學習。 基本概念
  1. /**
  2. *
  3. * 並查集,用來解決連通問題的,兩個節點之間是否是連通的。
  4. * 此處的節點是抽象的概念:比如使用者和使用者之間,港口和港口之間。
  5. * 用來看他們是否是連通的。典型的就是看你社交中任何人之間的關係是否認識.
  6. * 並查集問題和路徑問題的區別:並查集比路徑能做的操作少,它只能回答兩個節點是否連通
  7. * 路徑還可以找到類似最短的連通點等等.但正因為並查集專注於連線問題,所以判斷是否連線
  8. * 修改連線狀態時比較高效.
  9. *
  10. * Union-Find 如其名字我們主要提供
  11. * union(p,q); //合併p、q兩點使他們兩個連通.
  12. * find(p); //找到節點q的連通性,(處在什麼狀態合誰聯通)
  13. * 通過find的api,我們可以找到兩個節點是否會連通的,即api
  14. * isConnected(p,q);
  15. * @author zhaoyuan.
  16. *
  17. */
1、第一種實現:quick-find
union的時候每一個相等的都要改,如:1所以和2索引連通,那麼1、3、5、7、9索引 對應的id必須都變為0.
  1. //第一種基本的實現quick-find.
  2. public class UnionFind {
  3. //此處用一個id陣列來表示每個節點的連通性。
  4. //當節點連線到一起的時候那麼它們有相同的id號
  5. private int[] mIds;
  6. //表示描述的節點的規模,總共有多少個
  7. private int mCount;
  8. //構造中例項化用來儲存連通狀態的陣列,並初始化連通狀態
  9. //傳入的並查集要表示多少個元素
  10. public UnionFind(int capacity){
  11. mCount = capacity;
  12. mIds = new int[mCount];
  13. //初始為每個點都不連通,此處i不同就表示不連通,想要連通是就把i設定為同一個即可
  14. //同時也隱含著mCount各節點元素,每個節點元素的對應索引0...n,連通性,此處預設賦值都不連通
  15. for (int i = 0; i < mCount; i++) {
  16. mIds[i] = i+5;//注意id代表的含義不要和索引混了
  17. }
  18. }
  19. //尋找p索引對應的連通性的狀態,可以看到查詢某個元素的連通狀態碼
  20. //是非常的快的,直接在陣列中索引即可時間複雜度O(1)
  21. public int find(int p){
  22. if( p<0 || p>=mCount){
  23. //...做一些異常處理
  24. }
  25. //直接返回當前索引所對應的元素的連通性,
  26. //此處設計的是每個連通性預設是索引號.
  27. return mIds[p];
  28. }
  29. //此處設計是用的陣列儲存元素,傳入的是陣列內元素的索引,注意這個陣列不是指mIds.
  30. public boolean isConnected(int p,int q){
  31. //返回p和q在ids陣列中對應的連通狀態碼是否一致。
  32. return find(p) == find(q);
  33. }
  34. //聯合的整體思路:
  35. // 要麼把p索引在mIds中的狀態變成q的,
  36. // 要麼把q索引在mIds中的狀態變成p的
  37. //mIds中的狀態代表了連通性,id號相等就代表連通。
  38. //此時就遍歷mIds陣列,然後把p/q索引對應的id進行相關賦值
  39. public void union(int p,int q){
  40. //先拿到p和q的id
  41. int pId = find(p);
  42. int qId = find(q);
  43. //如果已經相等那麼直接返回
  44. if( pId == qId ){
  45. return;
  46. }
  47. //注意如下為什麼不直接mIds[p] = qId,不要被初始狀態迷惑
  48. //此處的設計思想quick-find查詢快,但想要改變連通性的時候
  49. //需要把所有的節點中的和pId相等的狀態碼,全部變成qId的狀態碼
  50. //只有這樣才能算是完全的連通了,你不能只改一個啊!!
  51. //這種設計模式下的union的時間複雜度是O(n).
  52. for(int i=0;i<mCount;i++){
  53. if(mIds[i] == pId){
  54. mIds[i] = qId;
  55. }
  56. }
  57. }
  58. }
這種實現最終整體的時間複雜度還是O(n)的級別,並且每次p、q連通的時,要改變所有連通id等於p的元素,把它們全都賦值成q對應的ids。 第二種實現:quick-union
初始的時候每個人的parent中的數指向自己的索引:  然後執行union (4 3):就是把索引4對應的元素的parent指向索引3.
 再比如:union(4,9)
找到4所在的屬性集合的根節點,然後連入到9所在的樹形結構的根節點,但此時9正好是根節點。  
  1. /**
  2. * 快速合併的並查集實現的思路:(還是用陣列)
  3. * 每個元素層都看成是一個節點,該節點有一個引用指向它的父節點。
  4. * 如果一個元素指向父節點那麼它就和父節點連通,當這個元素本身就是根的時候,
  5. * 那麼父節點就指向本身。
  6. * 由於當前只需一個用來儲存父節點的空間,所以依然可以用陣列來實現,此處用一個
  7. * int[] parent陣列,裡面存放的是元素要連通的父節點的索引.初始狀態都連線自己
  8. * 注意和quick-find區別,它表示連通的時可以 4--->3--->8 表示3 4 8都連通
  9. * 單用quick-find的時候,就需要 元素索引4、3 、8對應的id都為一樣的id號。
  10. *
  11. * @author zhaoyuan
  12. *
  13. */
  14. public class QuickUnion {
  15. private int[] mParents;
  16. private int mCount;
  17. public QuickUnion(int capacity){
  18. mCount = capacity;
  19. mParents = new int[mCount];
  20. //初始化時每個索引對應的mParents都為自己的索引+5,表示誰也不連線
  21. for (int i = 0; i < mCount; i++) {
  22. mParents[i] = i;//初始狀態為每個節點自己的索引
  23. }
  24. }
  25. //查詢索引p在parent中對應的連通狀態碼,當它是在一個樹的結構中時,
  26. //需要找到它一直往上直到根節點的對應碼,因為我們聯合的時候都是按照
  27. //根節點進行聯合的
  28. public int find(int p){
  29. if( p<0 || p>=mCount){
  30. //...做一些異常處理
  31. }
  32. //最根部的肯定是等於當前索引的.
  33. while(p!= mParents[p]){
  34. //依次往上,把指向的父索引值賦值給當前的p迴圈查詢.
  35. p = mParents[p];
  36. }
  37. return p;
  38. }
  39. //是否連通
  40. public boolean isConnected(int p,int q){
  41. return find(p)==find(q);
  42. }
  43. //聯合p所以和q索引對應的狀態.此處的設計:
  44. // 1)、把p所在的樹的根節點指向q所在的樹的根節點
  45. // 2)、把q所在的樹的根節點指向p所在的樹的根節點
  46. //但從此角度考慮的話兩種實現其實是一樣的
  47. public void union(int p,int q){
  48. //還是先找到pId和qId。
  49. int pRoot= find(p);
  50. int qRoot = find(q);
  51. //如果相等的時候,證明已經聯合
  52. if(pRoot == qRoot){
  53. return;
  54. }
  55. //第一版我們什麼也不考慮直接把p所在的樹的根節點pRoot指向q所在的樹的根。
  56. //所以注意不是mParents[p] = qRoot,應該是p索引找到的根
  57. //這個根肯定這會也是指向自己的元素的索引,直接mParents[pRoot]
  58. //把mParents中pRoot索引對應的值變成qRoot,也就是指向qRoot.
  59. mParents[pRoot] = qRoot;
  60. }
  61. }
這種實現測試發現聯合、判斷是否連線公共花費的時間不比quick-find少,因此我們進一步優化,讓樹的層次更 為平緩,我們在前面聯合的時候直接把pRoot的根指向了qRoot的根,這樣假如p這個樹的節點比較多的時候,把 它指向q所在的樹的時候,這個樹的深度會增加!因此我們可以以此為優化,把深度小的樹的根節點指向深度 大的樹的根節點,這樣讓整體更為扁平化。 就比如前面的union(4,9)
 我們可以把元素比較少的如:9的節點連線到元素比較多的4節點所在的根8上,這樣樹形圖的整體深度就不會增加。 第三種實現
  1. public class QuickUnionBetter {
  2. private int[] mParents;
  3. //新加一個數組用來記錄每一個節點,以它為根的元素的個數。
  4. 相關推薦

    演算法入門---java語言實現(Union-Find)小結

    圖片來自慕課網,僅僅為了記錄學習。 基本概念 /** * * 並查集,用來解決連通問題的,兩個節點之間是否是連通的。 * 此處的節點是抽象的概念:比如使用者和使用者之間,港口和港口之間。

    資料結構與演算法(十二)(Union Find)

    本文主要包括以下內容: 並查集的概念 並查集的操作 並查集的實現和優化 Quick Find Quick Union 基於size的優化 基於rank的優化 路徑壓縮優化 並查集的時間複雜度 並查集的概念 在電腦科學中,並查集 是一種樹形的資料結

    (Union-Find)演算法

    本文轉載自csdn另一博主,其原文點這裡。 public int find(int[] parent, int i) { if (parent[i] != i) { parent[i] = find(parent, pare

    (Union-Find)演算法詳解

    並查集(Union-Find)是解決動態連通性問題的一類非常高效的資料結構。本文中,我將盡我所能用最簡單,最清晰的邏輯展示出並查集的構造過程,同時還將對其中的關鍵步驟給出相應的Python程式碼。 動態連通性 可以想象一張地圖上有很多點,有些點之間是有道

    POJ 1611 The Suspects Union Find

    subset oid fin 由於 urn data tracking -m cts 本題也是個標準的並查集題解。 操作完並查集之後,就是要找和0節點在同一個集合的元素有多少。 註意這個操作,須要先找到0的父母節點。然後查找有多少個節點的額父母節點和0的父母節點同樣。

    (Union-Find)

    數組 樹根 情況 由於 指針 oid 父節點 要求 基本 一、基本操作: 1、Find:當且僅當兩個元素屬於相同的集合時,返回相同的名字 2、Union:將兩個不相交的集合合並為一個不想交的集合。 應用:在等價關系中,判斷兩個元素之間是否有關系或者添加等價關系。 二、基本數

    【LeetCode】 union-find(共16題)

    p.p1 { margin: 0.0px 0.0px 0.0px 0.0px; font: 12.0px Helvetica } 【128】Longest Consecutive Sequence  【130】Surrounded Regions  【200】Number of Is

    資料結構——Union Find

    一、並查集解決了什麼問題? 1、網路中節點間的連線狀態:這裡的網路是一個抽象的概念,指的是使用者之間形成的網路 2、兩個或兩個以上集合之間的交集 二、對並查集的設計 對於一組資料,主要支援兩個操作 public interface UnionFind {

    (Union-Find Sets)及其應用

    並查集 (Union-Find Sets)並查集:(union-find sets)是一種簡單的用途廣泛的集合. 並查集是若干個不相交集合,能夠實現較快的合併和判斷元素所在集合的操作,應用很多。一般採取樹形結構來儲存並查集,並利用一個rank陣列來儲存集合的深度下界,在查詢操

    (Union-Find) 應用舉例

    本文是作為上一篇文章 《並查集演算法原理和改進》 的後續,焦點主要集中在一些並查集的應用上。材料主要是取自POJ,HDOJ上的一些演算法練習題。 首先還是回顧和總結一下關於並查集的幾個關鍵點: 以樹作為節點的組織結構,結構的形態很是否採取優化策略有很大關係,未進行優化的樹結構可能會是“畸形”樹(嚴重不

    (Union-Find)粗略介紹

    並查集:一種樹型的資料結構,用於處理一些不相交集合(Disjoint Sets)的合併及查詢問題。常常在使用中以森林來表示。 集:就是讓每個元素構成一個單元素的集合,也就是按一定順序將屬於同一組的元素所在的集合合併。 例子:為了解釋並查集的原理,我將

    一、 (Union-Find Set)

    如果:給出各個元素之間的聯絡,要求將這些元素分成幾個集合,每個集合中的元素直接或間接有聯絡。在這類問題中主要涉及的是對集合的合併和查詢,因此將這種集合稱為並查集。  連結串列被普通用來計算並查集.表中的每個元素設兩個指標:一個指向同一集合中的下一個元素;另一個指向表首元素。  鏈結構的並查集  採用鏈式儲

    (Union-Find Sets)及其應用

    並查集 (Union-Find Sets) 並查集:(union-find sets)是一種簡單的用途廣泛的集合. 並查集是若干個不相交集合,能夠實現較快的合併和判斷元素所在集合的操作,應用很多。一般採取樹形結構來儲存並查集,並利用一個rank陣列來儲存集合的深度下界,在查

    (Union-Find Algorithm),看這一篇就夠了

    動態連線(Dynamic connectivity)的問題 所謂的動態連線問題是指在一組可能相互連線也可能相互沒有連線的物件中,判斷給定的兩個物件是否聯通的一類問題。這類問題可以有如下抽象: 有一組構成不相交集合的物件 union: 聯通兩個物件

    (union-find)模板

    #include<cstdio> #include<cstring> #include<vector> using namespace std; const int mx = 100005; int fa[mx], rk[mx]; ve

    (Union-Find

    在電腦科學中,並查集是一種樹型的資料結構,用於處理一些不交集(Disjoint Sets)的合併及查詢問題。有一個聯合-查詢演算法(union-find algorithm)定義了兩個用於此資料結構的操

    (Union-Find) 應用舉例 --- 基礎篇

    本文是作為上一篇文章 《並查集演算法原理和改進》 的後續,焦點主要集中在一些並查集的應用上。材料主要是取自POJ,HDOJ上的一些演算法練習題。 首先還是回顧和總結一下關於並查集的幾個關鍵點: 以樹作為節點的組織結構,結構的形態很是否採取優化策略有很大關係,未進行優化的

    POJ 1182 食物鏈 [資料結構- union-find sets]

       在輸入時可以先判斷題目所說的條件2和3,即:       1>若(x>n||y>n):即當前的話中x或y比n大,則假話數目num加1.       2>若(x==2&&x==y):即當前的話表示x吃x,則假話數目num加1.   而不屬於這兩種情況外的話語要利用

    『最小生成樹』Kruskal演算法——加邊法 (優化 + C++語言編寫 + 例題)

    『演算法原理』          在一個連通網的所有生成樹中,各邊的代價之和最小的那顆生成樹稱為該連通網的最小代價生成樹(Minimum Cost Spanning Tree),簡稱最小生成樹(MST)。         Kruskal演算法之所以叫加邊法,就是因為其本質

    C++實現

    將N個不同的元素分成一組不相交的集合。 開始時,每個元素就是一個集合,然後按規律將兩個集合進行合併。 假如已知有n個人和m對好友關係(存於陣列r),如果兩個人是直接的或間接的好友關係(好友的好