不相交集類演算法__資料結構

來源:互聯網
上載者:User

1、不相交集類的演算法:

這是處理等價問題的一種演算法,這裡的等價指的是集合,a與b等價,是指a∈S,b∈S。通過不相交集類的兩個基本操作find就可找出a和b的集合標誌是否一致,若一致則說明它們是一個集合的。當然,對於集合的表示,用的是森林的資料結構。集合的標誌就是根節點,若根節點一樣,就說明集合一樣。而除了唯一的根節點之外,剩下的節點裡面存放都是父節點的標誌。因此,我們只需要一個資料就能表示一個不相交集的結構了。不相交集類的另一個操作就是union。意圖是把兩個不屬於同一集合的元素,進行合并,使其等價




在union(int root1,int root2)操作中,合并的方式是有三種:

第一種是按照root1為根,root2為子節點的方式,也就是如果root1和root2合并的話,就需要把root2中原本存放的集合標誌,換成指向root1.

第二種是集合中節點數個數的大小來判斷誰合并誰;若root1的個數大於root2的個數,則root1為根,root2為節點;

第三種是集合中樹的高度的大小來判斷誰合并誰;若root1的高度大於root2的高度,則root1為根,root2為節點;

第二、三種之所以這麼做的原因是,按照第一種的方法,最終導致的是樹變得太深,每次的find操作消耗的時間長。通過控制個數或者高度,可以有效減緩樹變得越來越深。當然,最終樹的深度依舊會變大。嘗試著在find操作進行最佳化


find路徑壓縮演算法:

每次,在find(int x)找到跟節點之後,就將這個x添加到這個根節點的下面。那麼,下次尋找x的根的時候,其實,只要進行一次搜尋即可,也就意味這遞迴的次數將大大減少。這是犧牲了樹的廣度來減小樹的深度。事實上,這種方法很有效,因為,我們的不相交集類是通過一個一維數組來儲存的,廣度的大小根本就不需要在意




import java.util.ArrayList;/** * 不相交集類 *  * @author Holy-Spirit *  */public class DisjSet {private int length;private int[] array;/** * 初始化數組為-1 *  * 其中符號,代表的是這是根節點,也就是表示這個下標是這棵樹的標誌 符號後面的值為這棵樹節點的個數,或者是高度值 *  * 如果大於0,說明這是樹的子節點,裡面存放的是父節點的下標 *  * @param length *            數組的長度 */public DisjSet(int length) {if (length <= 0) {System.out.println("數組初始化長度有誤");return;}array = new int[length];this.length = array.length;for (int i = 0; i < array.length; i++) {array[i] = -1;}}/** * 擷取一個下標所在的整個集合的值 *  * @param index *            下標值 * @return 整個集合的List */public ArrayList<Integer> getDisjMember(int index) {int count = array[index] < 0 ? (array[index] * -1): (array[find(index)] * -1);ArrayList<Integer> list = new ArrayList<>();list.add(index);findAll(list, index, count);return list;}/** * @param list *            需要返回的List對象 * @param parentRoot *            父節點 * @param count *            這個集合的節點個數 * @return 集合List */private ArrayList<Integer> findAll(ArrayList<Integer> list, int parentRoot,int count) {if (count == 0) {return list;}for (int i = 0; i < array.length; i++) {if (array[i] == parentRoot) {list.add(i);findAll(list, i, count--);}}return list;}/** * 擷取這個數組長度 *  * @return */public int length() {return this.length;}/** * 判斷是否全部合并 *  * @return */public boolean isUnionAll() {boolean isUnionAll = false;for (int i = 0; i < array.length; i++) {if (array[i] < 0) {if (isUnionAll) {isUnionAll = !isUnionAll;break;}isUnionAll = !isUnionAll;}}return isUnionAll;}/** * 通過比較兩個集合的個數來決定誰被歸併 每次歸併,在根節點,存放整個集合中節點的個數 *  * @param root1 * @param root2 */public void unionBySzie(int root1, int root2) {int sumSize = array[root1] + array[root2];if (array[root1] > array[root2]) {array[root2] = sumSize;array[root1] = root2;} else {array[root1] = sumSize;array[root2] = root1;}}/** * 通過集合的高度進行歸併 *  * 高度小的集合稱為高度大的樹的子節點 *  * @param root1 * @param root2 */public void unionByHeigh(int root1, int root2) {if (array[root1] < array[root2]) {array[root1] = root2;} else {if (array[root1] == array[root2]) {array[root1]--;}array[root2] = root1;}}/** * 路徑壓縮的尋找方式 通過根的下標來標識樹集合 *  * 路徑壓縮的思想是,union的最後或多或少會破壞一棵樹的平衡性 所以每次 * find後,都將這個find的位置中存放的父節點的下標值改成根節點的下標,這樣一來,樹的高度將變小。但是 *  * @param x * @return 最終返回的是這個集合的根節點,因為根節點中存放著這棵樹的大小或者高度 */public int find(int x) {if (array[x] < 0) {return x;} else {return array[x] = find(array[x]);}}}


不相交集類的思想在很多地方都有用處,比如要產生一個迷宮...







聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.