1. 程式人生 > >無迴路有向圖的拓撲排序

無迴路有向圖的拓撲排序

因公司業務需要,在表單中每個欄位都會配置自動計算,但自動計算公式中會引用到其他欄位中的值。所以希望可以根據計算公式,優先計算引用的公式。所以最終使用了無迴路有向圖的擴撲排序來實現。

直接上程式碼,後邊講解實現思路。

/**
 * 無迴路有向圖(Directed Acyclic Graph)的拓撲排序
 * 該DAG圖是通過鄰接表實現的。
 *
 * @author lyz
 */
public class FieldListDG {
    /**
     * 鄰接表中表對應的連結串列的頂點
     */
    private class ENode {
        int ivex;       // 該邊所指向的頂點的位置
        ENode nextEdge; // 指向下一條弧的指標
    }

    /**
     * 鄰接表中表的頂點
     */
    private class VNode {
        String data;          // 頂點資訊
        ENode firstEdge;    // 指向第一條依附該頂點的弧
    }

    /**
     * 頂點陣列
     */
    private List<VNode> mVexs;


    /**
     * 建立圖(用已提供的矩陣)
     * <p>
     * 引數說明:
     * vexs  -- 頂點陣列
     * edges -- 邊陣列
     */
    public FieldListDG(List<String> vexs, List<List<String>> edges) {

        // 初始化"頂點數"和"邊數"
        int vlen = vexs.size();
        int elen = edges.size();

        // 初始化"頂點"
        mVexs = new ArrayList<>();
        for (int i = 0; i < vlen; i++) {
            // 新建VNode
            VNode vnode = new VNode();
            vnode.data = vexs.get(i);
            vnode.firstEdge = null;
            // 將vnode新增到陣列mVexs中
            mVexs.add(vnode);
        }

        // 初始化"邊"
        for (int i = 0; i < elen; i++) {
            // 讀取邊的起始頂點和結束頂點
            int p1 = getPosition(edges.get(i).get(0));
            int p2 = getPosition(edges.get(i).get(1));

            // 初始化node1
            ENode node1 = new ENode();
            node1.ivex = p2;
            // 將node1連結到"p1所在連結串列的末尾"
            if (mVexs.get(p1).firstEdge == null) {
                mVexs.get(p1).firstEdge = node1;
            } else {
                linkLast(mVexs.get(p1).firstEdge, node1);
            }
        }
    }

    /**
     * 將node節點連結到list的最後
     */
    private void linkLast(ENode list, ENode node) {
        ENode p = list;

        while (p.nextEdge != null) {
            p = p.nextEdge;
        }
        p.nextEdge = node;
    }

    /**
     * 返回String位置
     */
    private int getPosition(String str) {
        for (int i = 0; i < mVexs.size(); i++) {
            if (mVexs.get(i).data.equals(str)) {
                return i;
            }
        }
        return -1;
    }

    /**
     * 拓撲排序
     * <p>
     * 返回值:
     * -1 -- 失敗(由於記憶體不足等原因導致)
     * 0 -- 成功排序,並輸入結果
     * 1 -- 失敗(該有向圖是有環的)
     */
    public List<String> topologicalSort() {
        int index = 0;
        int num = mVexs.size();
        int[] ins;               // 入度陣列
        String[] tops;             // 拓撲排序結果陣列,記錄每個節點的排序後的序號。
        Queue<Integer> queue;    // 輔組佇列

        ins = new int[num];
        tops = new String[num];
        queue = new LinkedList<>();

        // 統計每個頂點的入度數
        for (int i = 0; i < num; i++) {

            ENode node = mVexs.get(i).firstEdge;
            while (node != null) {
                ins[node.ivex]++;
                node = node.nextEdge;
            }
        }

        // 將所有入度為0的頂點入佇列
        for (int i = 0; i < num; i++) {
            if (ins[i] == 0) {
                // 入佇列
                queue.offer(i);
            }
        }
        // 佇列非空
        while (!queue.isEmpty()) {
            // 出佇列。j是頂點的序號
            int j = queue.poll().intValue();
            // 將該頂點新增到tops中,tops是排序結果
            tops[index++] = mVexs.get(j).data;
            // 獲取以該頂點為起點的出邊佇列
            ENode node = mVexs.get(j).firstEdge;

            // 將與"node"關聯的節點的入度減1;
            // 若減1之後,該節點的入度為0;則將該節點新增到佇列中。
            while (node != null) {
                // 將節點(序號為node.ivex)的入度減1。
                ins[node.ivex]--;
                // 若節點的入度為0,則將其"入佇列"
                if (ins[node.ivex] == 0) {
                    // 入佇列
                    queue.offer(node.ivex);
                }

                node = node.nextEdge;
            }
        }

        if (index != num) {
            return Collections.emptyList();
        }
        List<String> labelList = Arrays.asList(tops);
        Collections.reverse(labelList);
        return labelList;
    }

    /**
     * 測試用例
     */
    public static void main(String[] args) {
        List<String> vexs = new ArrayList<>();
        vexs.add("a");
        vexs.add("b");
        vexs.add("c");
        vexs.add("d");
        vexs.add("e");
        vexs.add("f");
        vexs.add("g");

        List<List<String>> edges = new ArrayList<>();
        List<String> node = new ArrayList<>();
        node.add("a");
        node.add("g");
        edges.add(node);

        List<String> node1 = new ArrayList<>();
        node1.add("a");
        node1.add("c");
        edges.add(node1);

        List<String> node2 = new ArrayList<>();
        node2.add("b");
        node2.add("a");
        edges.add(node2);

        List<String> node3 = new ArrayList<>();
        node3.add("b");
        node3.add("d");
        edges.add(node3);

        List<String> node4 = new ArrayList<>();
        node4.add("c");
        node4.add("f");
        edges.add(node4);

        List<String> node5 = new ArrayList<>();
        node5.add("c");
        node5.add("g");
        edges.add(node5);

        List<String> node6 = new ArrayList<>();
        node6.add("d");
        node6.add("e");
        edges.add(node6);

        List<String> node7 = new ArrayList<>();
        node7.add("d");
        node7.add("f");
        edges.add(node7);

        long start = System.currentTimeMillis();

        for (int i = 0; i < 10; i++) {
            FieldListDG pG = new FieldListDG(vexs, edges);
            // 拓撲排序
            List<String> list = pG.topologicalSort();
        }
        System.out.println("共計時間" + (System.currentTimeMillis() - start));

//        list.forEach(e -> System.out.println(e));

    }
}

無迴路有向圖拓撲排序

相關推薦

溫習Algs4 (四):, 排序和強連通分量

有向圖, 拓撲排序和強連通分量 有向圖 Digraph.java 有向環 DiCycle.java 深度優先搜尋序列 DFSOrder.java 拓撲排序 Topo

迴路排序

因公司業務需要,在表單中每個欄位都會配置自動計算,但自動計算公式中會引用到其他欄位中的值。所以希望可以根據計算公式,優先計算引用的

判斷是否環及排序

對一個有向無環圖(Directed Acyclic Graph簡稱DAG)G進行拓撲排序,是將G中所有頂點排成一個線性序列,使得圖中任意一對頂點u和v,若邊(u,v)∈E(G),則u線上性序列中出現在v之前。通常,這樣的線性序列稱為滿足拓撲次序(Topological O

判斷是否環之排序-LeetCode 207. Course Schedule

拓撲排序:對一個有向無環圖(Directed Acyclic Graph簡稱DAG)G進行拓撲排序,是將G中所有頂點排成一個線性序列,使得圖中任意一對頂點u和v,若邊(u,v)∈E(G),則u線上性序列中出現在v之前。 下圖是一個拓撲排序: 下圖不是一個拓撲排序:如何獲得

BZOJ_1916_[Usaco2010 Open]沖浪_分層+排序+DP

src 找到 -- 無法 不可 EDA image 否則 她是 BZOJ_1916_[Usaco2010 Open]沖浪_分層圖+拓撲排序+DP Description 受到秘魯的馬丘比丘的新式水上樂園的啟發,Farmer John決定也為奶牛們建 一個水上樂園。當然,

【LuoguP4934】禮物(LGR-054)-Dilworth定理+優化建+排序

測試地址:禮物 做法: 本題需要用到Dilworth定理+優化建圖+拓撲排序。 對位運算感覺比較敏銳的話,可以看出, a &amp;

第七章--排序與關鍵路徑-計算機17級

解析在下面  解析: x2-1:這個就是定義,最長的路徑 x2-2: 這個補充一個知識: 一個小補充: 分別用佇列和堆疊作為容器,對計算機專業課程進行拓撲排序,得到的序列有什麼區別?用哪種容器排課更合理? 答案: 根據棧和佇列的特性可以

NOIP2017 逛公園 分層+排序

就快把NOIP的題都做完了(事實上剩下的題都是最毒瘤的,比如天天愛跑步 正解暫時還不會寫,在這裡先貼一個分層圖的題解(會被卡30分,Luogu上開O2才能過 分層圖: 由於k<=50k<=50,把每個點拆成k+1k+1個點,表示經過該點時,超出

2018.10.10 bzoj1565: [NOI2009]植物大戰殭屍(最大權閉合子+排序

傳送門 由題可以得出一些關係。 如果對於同一行的相鄰兩個格子(i,j),(i,j+1)(i,j),(i,j+1)(i,j),(i,j+1),那麼前者是後者的後繼。 如果對於兩個格子A(a,b),B(c,d)A(a,b),B(c,d)A(a,b),B(c,d),

排序的兩種方法實現

方法一: (1)在有向圖中選一個沒有前驅(入度為0)的點輸出。 (2)從圖中刪除該頂點和所有以它為尾的弧。 重複以上步驟,直至全部頂點均已輸出,或者當前圖中不存在五前驅的頂點為止。 在實現中,我們可以用一個佇列存入所有入度為0的頂點。然後依次刪除這些頂點,和其對

洛谷3953 NOIP2017 逛公園 最短路+排序+dp

題目連結 題意: 給你一個n個點m條邊的有向帶權圖,設1號點到n號點的最短路是dis,給你一個k(k<=50),求所有1到n的路徑中長度不超過dis+k的數量。 題解: 顯然我們要先處理出最短路,以後再也不會寫SPFA了,因為NOI2018卡了SP

-----------排序+AOE網路關鍵路徑

1.拓撲排序(1)舉個例子,要學習某些課程必須先學過一些課程用圖把這個東東描述出來就變成:那麼,問題來啦,是否可以找到一個序列,使得這個序列上的所有課程都滿足:先修課程在後修的課程前面?這樣的序列就是拓撲序列.....(2)怎麼求拓撲序列?簡單的說是不斷去掉沒有前驅的點,得到

[算法小練][][排序+深度優先搜索] 平板塗色問題

png 不同的 string %d 程序 using class info 完整 說在前面 本題是一道經典題目,多做經典題目可以節省很多學習時間,比如本題就包含了許多知識:回溯+剪枝+拓撲排序+深度優先搜索。[動態規劃方法另作討論] 關鍵代碼 題: CE

DAG 排序 程式碼解釋

目錄: DAG定義 舉例描述 實際運用 演算法描述 演算法實戰 演算法視覺化 定義 在圖論中,由一個有向無環圖的頂點組成的序列,當且僅當滿足下列條件時,稱為該圖的一個拓撲排序(英語:Topological sorting)。 每個頂點出現且只出現一

實驗四(建+鄰接矩陣(BFS,DFS(遞迴+非遞迴)),+鄰接表(BFS,DFS(遞迴+非遞迴)),排序

//Sinhaeng Hhjian #include<bits/stdc++.h> using namespace std; const int N=100; const int MAX=1000; int book[N], cnt; struct node{

->->排序

文字描述   關於有向無環圖的基礎定義:     一個無環的有向圖稱為有向無環圖,簡稱DAG圖(directed acycline graph)。DAG圖是一類較有向樹更一般的特殊有向圖。        舉個例子說明有向無環圖的應用。假如有一個表示式: ((a+b)*(b*(c+d))+(c+d)*e

排序+最短路徑(環加權最短路徑演算法)

特點:        1、線性時間內解決單點最短路徑問題        2、能夠處理負權邊問題        3、能夠找出最長路徑 不足:因為是基於拓撲排序的,所以不能解決帶環的問題  import java.util.ArrayList; import java.util

排序(判斷是否是

要進行拓撲排序之前,該圖要是有向無環圖。 排序方法: 1、從有向圖中選取一個沒有前驅的頂點,並輸出之 ;2、從有向圖中刪去此頂點以及所有以它為尾的弧; 3、重複上述兩步,直至圖空,或者圖不空但找不到無前驅的頂點為止。 #include<stdio.h>

判斷是否存在迴路排序

#include <iostream> #include <string> #include <queue> using namespace std; typedef struct Edgenode { int adjvex; st

演算法: (DAG)的排序

更新: 拓撲排序有2中方法(最後結果可能不同,因為拓撲排序有多解)。 一個簡單的求拓撲排序的演算法是先找出任意一個沒有入邊的頂點,然後將它和它的邊從圖中刪除。然後對剩餘部分使用同樣的操作。 public ArrayList<Integer&g