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