並查集(Union Find)

一、關於查詢和連接數據結構的比較

動態數組 鏈表 平衡二叉樹 Set 並查集
查詢是否在同一村莊的時間複雜度 O(n) O(n) O(n)---需要遍歷每棵樹的所有結點 O(n) O(k)
合併兩個村莊的時間複雜度 O(n) O(n) O(n)---需要將一棵樹上所有結點都挪到另一棵樹上 O(n) O(k)




二、並查集的設計即實現

1、存儲方式設計

       可以用結點數組存儲並查集,並查集其實也是用數組實現的樹形結構,和之前的二叉堆及優先隊列類似。初始化的時候每個結點都看成一個單獨的集合,並且默認父親就是指向自己

A

2、怎麼合併結點

       每次都先找到待合併兩個結點的根結點,將兩個根節點合併即可。


       不過這裏可以分別對合併過程、查詢過程進行相應優化

       合併第一種優化方式可以是將結點少的集合併到結點多的集合下面。
Alt

       合併第二種就是將高度低的集合併到高度高的集合下面。
Alt

       查詢第一種優化方式路徑分裂,即將路徑上每一個結點分別指向自己的祖父結點。

Alt


       查詢第二種優化方式路徑減半,即將路徑上每隔一個結點就指向自己的祖父結點。
Alt

3、實現

       採用高度優化和路徑減半。

	public class MyUnionFind<E> {
	    private Map<E, Node<E>> nodes;
	
	    private static class Node<E> {
	        Node<E> parent = this;     //新建的結點默認就是根結點, 自己指向自己
	        E element;
	        int height;
	
	        public Node(E element) {
	            this.element = element;
	        }
	    }
	
	    public MyUnionFind() {
	        nodes = new HashMap<>();
	    }
	
	    public void makeSet(E e) {
	        if (nodes.containsKey(e))
	            return;
	        nodes.put(e, new Node<>(e));
	    }
	
	    /**
	     * 根據某個結點的對象找到根結點的對象
	     *
	     * @param e
	     * @return
	     */
	    public E find(E e) {
	        Node<E> node = findNode(e);
	        return node == null ? null : node.element;
	    }
	
	    /**
	     * 根據某個結點的對象找到根結點
	     *
	     * @param e
	     * @return
	     */
	    private Node<E> findNode(E e) {
	        Node<E> node = nodes.get(e);
	        if (node == null)
	            return null;
	
	        //Path Halving優化
	        while (!Objects.equals(node.element, node.parent.element)) {      //根據結點中的對象來比較
	            node.parent = node.parent.parent;
	            node = node.parent;
	        }
	        return node;
	    }
	
	    public void union(E e1, E e2) {
	        Node<E> r1 = findNode(e1);
	        Node<E> r2 = findNode(e2);
	        if (Objects.equals(r1.element, r2.element))
	            return;
	
	        if (r1.height > r2.height)
	            r2.parent = r1;
	        else if (r1.height < r2.height)
	            r1.parent = r2;
	        else {
	            r1.parent = r2;
	            r2.height++;
	        }
	    }
	
	    public boolean isSame(E e1, E e2) {
	        return find(e1) == find(e2);
	    }
	}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章