無迴路有向圖的拓撲排序

因公司業務須要,在表單中每一個字段都會配置自動計算,但自動計算公式中會引用到其餘字段中的值。因此但願能夠根據計算公式,優先計算引用的公式。因此最終使用了無迴路有向圖的擴撲排序來實現。java

直接上代碼,後邊講解實現思路。node

/**
 * 無迴路有向圖(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));

    }
}

無迴路有向圖拓撲排序

相關文章
相關標籤/搜索