日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 综合教程 >内容正文

综合教程

【算法导论-36】并查集(Disjoint Set)具体解释

發布時間:2024/8/26 综合教程 29 生活家
生活随笔 收集整理的這篇文章主要介紹了 【算法导论-36】并查集(Disjoint Set)具体解释 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

WiKi

Disjoint是“不相交”的意思。Disjoint Set高效地支持集合的合并(Union)和集合內元素的查找(Find)兩種操作,所以Disjoint Set中文翻譯為并查集。
就《算法導論》21章來講,主要設計這幾個知識點:
? 用并查集計算圖的連通區域;
? 推斷兩個頂點是否屬于同一個連通區域;
? 鏈表實現并查集;
? Rooted tree實現并查集;
? Rooted tree實現并查集時採用rank方法和路徑壓縮算法。
《算法導論》21.4給出了一個結論:總計m個MAKE-SET、UNION、FIND-SET操作。當中MAKE-SET的個數為n,則採用rank和路徑壓縮算法實現的并查集最壞時間復雜度是O(m α(n) )。當中α是Ackerman函數的某個反函數,這個函數的值能夠看成是不大于4。所以,并查集的三種典型操作的時間復雜度是線性的

相關資料

并查集的維基百科

并查集的java實現

這里依據《算法導論》的21.3節的偽代碼,實現了一個泛型的并查集。輸出時,打印節點及其集合的代表元素(即根元素。representative)。

import java.util.ArrayList;
import java.util.List;
import java.util.TreeSet;

/**
 * <p>并查集的實現<p/>
 * <p>參考:《算法導論》21.3節<p/>
 * <p>created by 曹艷豐<p/>
 * <p>2016-08-31<p/>
 * 
 * */
public class DisjointSet<T> {
    private List<Node> forests;//全部節點
    public DisjointSet(){
        forests=new ArrayList<Node>();
    }
    /**
     * 內部類,并查集的rooted node
     * */

    private class Node{
        Node parent;
        int rank;
        T t;
        private Node(T t){
            parent=this;
            rank=0;
            this.t=t;
        }
    }
    //向森林中加入節點
    public void makeSet(T t){
        Node node=new Node(t); 
        forests.add(node);
    }
    //將包括x和包括y的兩個集合進行合并
    public void union(T x,T y){
        Node xNode=isContain(x);
        Node yNode=isContain(y);
        if (xNode!=null&&yNode!=null) {
            link(findSet(xNode), findSet(yNode));
        }
    }
    //查找到節點node的根節點
    public Node findSet(Node node){
        if (node!=node.parent) {
            //路徑壓縮,參考《算法導論》插圖21.5
            node.parent=findSet(node.parent);
        }
        return node.parent;
    }
    //查找到節點node的根節點
        public Node findSet(T t){
            Node node=isContain(t);
            if (node==null) {
                throw new IllegalArgumentException("不含該節點!

"); }else { return findSet(node); } } //將兩個根節點代表的集合進行連接 private void link(Node xNode,Node yNode){ if (xNode.rank>yNode.rank) { yNode.parent=xNode; }else { xNode.parent=yNode; if (xNode.rank==yNode.rank) { yNode.rank+=1; } } } //森林是否包括這個節點 private Node isContain(T t){ for (Node node : forests) { if (node.t.equals(t)) { return node; } } return null; } @Override public String toString() { // TODO Auto-generated method stub if (forests.size()==0) { return "并查集為空!"; } StringBuilder builder=new StringBuilder(); for (Node node : forests) { Node root=findSet(node); builder.append(node.t).append("→").append(root.t); builder.append(" "); } return builder.toString(); } }

然后測試一下

public class Main{

    public static void main(String[] args) {
        // TODO Auto-generated method stub

        DisjointSet<String> disjointSet=new DisjointSet<String>();
        disjointSet.makeSet("cao");
        disjointSet.makeSet("yan");
        disjointSet.makeSet("feng");
        disjointSet.union("cao", "yan");
        disjointSet.union("cao", "feng");
        System.out.println(disjointSet.toString());
    }
}

輸出格式,元素→代表元素

cao→yan
yan→yan
feng→yan

表明3個節點的代表元素一致。即處于一個集合中。

圖的連通區域計算`

《算法導論》21.1節的偽代碼。這里給出連通區域計算的樣例。圖的數據結構採用“【算法導論-35】圖算法JGraphT開源庫介紹 “中的無向圖。

private static void connectedComponents(){
        UndirectedGraph<String, DefaultEdge> g =
                new SimpleGraph<>(DefaultEdge.class);

        String v1 = "v1";
        String v2 = "v2";
        String v3 = "v3";
        String v4 = "v4";

        // add the vertices
        g.addVertex(v1);
        g.addVertex(v2);
        g.addVertex(v3);
        g.addVertex(v4);

        // add edges to create a circuit
        g.addEdge(v1, v2);
        g.addEdge(v2, v3);

        //連通區域計算
        //參考《算法導論》21.1節
        DisjointSet<String> disjointSet=new DisjointSet<String>();
        for ( String v : g.vertexSet()) {
            disjointSet.makeSet(v);
        }

//        for ( DefaultEdge e : g.edgeSet()) {
//          String source=e.getSource();//protected訪問類型
//          String target=e.getTarget();//protected訪問類型
//          if (disjointSet.findSet(source)!=disjointSet.findSet(target)) {
//              disjointSet.union(source, target);
//          }
//      }

        if (disjointSet.findSet(v1)!=disjointSet.findSet(v2)) {
            disjointSet.union(v1, v2);
        }
        if (disjointSet.findSet(v2)!=disjointSet.findSet(v3)) {
            disjointSet.union(v2, v3);
        }
        System.out.println(disjointSet.getSetCounter());

    }

輸出

v1→v2
v2→v2
v3→v2
v4→v4

v1、v2、v3的代表元素一致。表明三者在一個集合中,即三者連通。

v4是另外一個集合。

實例應用

舉個樣例,某人結婚時宴請賓客,A來賓認識B來賓,B來賓認識C來賓,則A、B、C安排在一桌。

A來賓認識B來賓,且A、B的熟人及其熟人的熟人(熟人鏈)不包括C,則C與A、B不在一桌。問。須要多少桌子才干滿足要求呢?
這個樣例事實上就是連通區域的詳細到社交關系的1度、2度……n度關系。

略微改動并查集的實例,加入集合的計數setCounter,每次makeset時遞增,union時遞減。這樣就得到最后的集合個數。

import java.util.ArrayList;
import java.util.List;
import java.util.TreeSet;

/**
 * <p>并查集的實現<p/>
 * <p>參考:《算法導論》21.3節<p/>
 * <p>created by 曹艷豐<p/>
 * <p>2016-08-31<p/>
 * 
 * */
public class DisjointSet<T> {
    private List<Node> forests;//全部節點
    private int setCounter;//集合計數
    public DisjointSet(){
        forests=new ArrayList<Node>();
        setCounter=0;
    }

    public int getSetCounter() {
        return setCounter;
    }

    /**
     * 內部類,并查集的rooted node
     * */

    private class Node{
        Node parent;
        int rank;
        T t;
        private Node(T t){
            parent=this;
            rank=0;
            this.t=t;
        }
    }
    //向森林中加入節點
    public void makeSet(T t){
        Node node=new Node(t); 
        forests.add(node);
        setCounter++;
    }
    //將包括x和包括y的兩個集合進行合并
    public void union(T x,T y){
        if (x.equals(y)) {
            throw new IllegalArgumentException("Union的兩個元素不能相等。");
        }
        Node xNode=isContain(x);
        Node yNode=isContain(y);
        if (xNode!=null&&yNode!=null) {
            link(findSet(xNode), findSet(yNode));
            setCounter--;
        }
    }
    //查找到節點node的根節點
    public Node findSet(Node node){
        if (node!=node.parent) {
            //路徑壓縮,參考《算法導論》插圖21.5
            node.parent=findSet(node.parent);
        }
        return node.parent;
    }
    //查找到節點node的根節點
        public Node findSet(T t){
            Node node=isContain(t);
            if (node==null) {
                throw new IllegalArgumentException("不含該節點!

"); }else { return findSet(node); } } //將兩個根節點代表的集合進行連接 private void link(Node xNode,Node yNode){ if (xNode.rank>yNode.rank) { yNode.parent=xNode; }else { xNode.parent=yNode; if (xNode.rank==yNode.rank) { yNode.rank+=1; } } } //森林是否包括這個節點 private Node isContain(T t){ for (Node node : forests) { if (node.t.equals(t)) { return node; } } return null; } @Override public String toString() { // TODO Auto-generated method stub if (forests.size()==0) { return "并查集為空!"; } StringBuilder builder=new StringBuilder(); for (Node node : forests) { Node root=findSet(node); builder.append(node.t).append("→").append(root.t); builder.append(" "); } return builder.toString(); } }

連通區域的計算,只是這里輸出的是集合個數。

private static void connectedComponents(){
        UndirectedGraph<String, DefaultEdge> g =
                new SimpleGraph<>(DefaultEdge.class);

        String v1 = "v1";
        String v2 = "v2";
        String v3 = "v3";
        String v4 = "v4";

        // add the vertices
        g.addVertex(v1);
        g.addVertex(v2);
        g.addVertex(v3);
        g.addVertex(v4);

        // add edges to create a circuit
        g.addEdge(v1, v2);
        g.addEdge(v2, v3);

        //連通區域計算
        //參考《算法導論》21.1節
        DisjointSet<String> disjointSet=new DisjointSet<String>();
        for ( String v : g.vertexSet()) {
            disjointSet.makeSet(v);
        }

//        for ( DefaultEdge e : g.edgeSet()) {
//          String source=e.getSource();//protected訪問類型
//          String target=e.getTarget();//protected訪問類型
//          if (disjointSet.findSet(source)!=disjointSet.findSet(target)) {
//              disjointSet.union(source, target);
//          }
//      }

        if (disjointSet.findSet(v1)!=disjointSet.findSet(v2)) {
            disjointSet.union(v1, v2);
        }
        if (disjointSet.findSet(v2)!=disjointSet.findSet(v3)) {
            disjointSet.union(v2, v3);
        }
        System.out.println(disjointSet.getSetCounter());

    }

輸出是2。

總結

以上是生活随笔為你收集整理的【算法导论-36】并查集(Disjoint Set)具体解释的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。

歡迎分享!

轉載請說明來源于"生活随笔",并保留原作者的名字。

本文地址:【算法导论-36】并查集(Disjoint Set)具体解释