那麼什麼是並查集呢?java
並查集被不少OIer認爲是最簡潔而優雅的數據結構之一,主要用於解決一些元素分組的問題。它管理一系列不相交的集合,並支持兩種操做:數組
固然,這樣的定義未免太過學術化,看完後恐怕不太能理解它具體有什麼用。因此咱們先來看看並查集最直接的一個應用場景:親戚問題。數據結構
(洛谷P1551)親戚ide
題目背景
若某個家族人員過於龐大,要判斷兩個是不是親戚,確實還很不容易,如今給出某個親戚關係圖,求任意給出的兩我的是否具備親戚關係。
題目描述
規定:x和y是親戚,y和z是親戚,那麼x和z也是親戚。若是x,y是親戚,那麼x的親戚都是y的親戚,y的親戚也都是x的親戚。
輸入格式
第一行:三個整數n,m,p,(n<=5000,m<=5000,p<=5000),分別表示有n我的,m個親戚關係,詢問p對親戚關係。
如下m行:每行兩個數Mi,Mj,1<=Mi,Mj<=N,表示Mi和Mj具備親戚關係。
接下來p行:每行兩個數Pi,Pj,詢問Pi和Pj是否具備親戚關係。
輸出格式
P行,每行一個’Yes’或’No’。表示第i個詢問的答案爲「具備」或「不具備」親戚關係。
這實際上是一個頗有現實意義的問題。咱們能夠創建模型,把全部人劃分到若干個不相交的集合中,每一個集合裏的人彼此是親戚。爲了判斷兩我的是否爲親戚,只需看它們是否屬於同一個集合便可。所以,這裏就能夠考慮用並查集進行維護了。spa
1 import java.util.HashSet; 2 import java.util.Scanner; 3 import java.util.Set; 4 5 public class MergePlant { 6 7 /** 8 * 查找 9 * @param a 待查找的節點 10 * @param parent 存儲全部節點對應的根節點的數組 11 * @return 返回根節點的序號,從1開始 12 */ 13 public static int find(int a, int[] parent) { 14 int root = a; 15 while (parent[root] != 0) { 16 root = parent[root]; 17 } 18 return root; 19 } 20 21 /** 22 * 合併 23 * @param a 合併的第一個節點 24 * @param b 合併的第二個節點 25 * @param parent 存儲根節點的數組 26 * @param rank 存儲樹的等級,最小爲0,即只有一個節點自己 27 * @return 返回 true 則表示合併成功,返回 false 則表示合併失敗,即兩個節點是在同一棵樹上 28 */ 29 public static boolean union(int a, int b, int[] parent, int[] rank) { 30 int root_a = find(a, parent); 31 int root_b = find(b, parent); 32 33 if (root_a == root_b) { 34 return false; 35 } else { 36 if (rank[root_a] > rank[root_b]) { 37 // a節點所在的樹的等級 > b節點所在的樹的等級 38 // 將b節點所在的樹合併到a節點所在的樹上 39 parent[root_b] = root_a; 40 } else if (rank[root_a] < rank[root_b]) { 41 // a節點所在的樹的等級 < b節點所在的樹的等級 42 // 將a節點所在的樹合併到b節點所在的樹上 43 parent[root_a] = root_b; 44 } else { 45 // 若是相等,只需隨便選一個樹合併到另一棵樹上便可,這裏就讓a合併到b 46 // b節點所在樹的等級要+1 47 parent[root_a] = root_b; 48 rank[root_b]++; 49 } 50 return true; 51 } 52 } 53 54 /** 55 * 將全部節點的初始根節點都設置爲自身,即爲0,方便後面查找根節點 56 * 將全部樹的等級一開始都設置爲0 57 * @param parent 存儲根節點的數組 58 */ 59 public static void initialization(int[] parent, int[] rank) { 60 for (int i = 0; i < parent.length; i++) { 61 parent[i] = 0; 62 rank[i] = 0; 63 } 64 } 65 66 public static void main(String[] args) { 67 Scanner input = new Scanner(System.in); 68 int m = input.nextInt(); 69 int n = input.nextInt(); 70 71 // 定義根節點數組 72 int[] parent = new int[m * n + 1]; 73 // 定義樹等級數組 74 int[] rank = new int[m * n + 1]; 75 76 // initialization to -1 77 initialization(parent, rank); 78 79 // 接收邊的數量 80 int side = input.nextInt(); 81 // 接收邊,即節點與節點之間的關係 82 // 來一個關係,合併一次 83 for (int i = 0; i < side; i++) { 84 int a = input.nextInt(); 85 int b = input.nextInt(); 86 union(a, b, parent, rank); 87 } 88 89 // 最後判斷存儲根節點的數組中還剩下幾棵樹,這裏用Set集合來存儲,爲了去重 90 Set<Integer> set = new HashSet<>(); 91 // 編號從1 ~ m * n 92 for (int i = 1; i <= m * n; i++) { 93 set.add(find(i, parent)); 94 } 95 96 System.out.println(set.size()); 97 } 98 }