無向圖入口、出口遍歷算法

無向圖遍歷算法

最近遇到一個需要遍歷無向圖的算法,需要從圖的某一個結點開始遍歷,找出到某一類結點結束的所有路徑。網上找了很多算法,都不是很靠譜。後來發現這個比較靠譜。整理如下:

算法目標:

在一個無向連通圖中求出兩個給定點之間的所有路徑;

算法思路:

  1. 整理節點間的關係,爲每個節點建立一個集合,該集合中保存所有與該節點直接相連的節點(不包括該節點自身);

  2. 定義兩點一個爲起始節點,另一個爲終點,求解兩者之間的所有路徑的問題可以被分解爲如下所述的子問題:對每一 個與起始節點直接相連的節點,求解它到終點的所有路徑(路徑上不包括起始節點)得到一個路徑集合,將這些路徑集合相加就可以得到起始節點到終點的所有路徑;依次類推就可以應用遞歸的思想,層層遞歸直到終點,若發現希望得到的一條路徑,則轉儲並打印輸出;若發現環路,或發現死路,則停止尋路並返回;

  3. 用棧保存當前已經尋到的路徑(不是完整路徑)上的節點,在每一次尋到完整路徑時彈出棧頂節點;而在遇到從棧頂節點無法繼續向下尋路時也彈出該棧頂節點,從而實現回溯。

代碼及測試如下:

import java.util.*;

public class test {
    /* 臨時保存路徑節點的棧 */
    public static Stack<Node> stack = new Stack<Node>();
    /* 存儲路徑的集合 */
    public static ArrayList<Node[]> sers = new ArrayList<>();

    /* 判斷節點是否在棧中 */
    public static boolean isNodeInStack(Node node) {
        Iterator<Node> it = stack.iterator();
        while (it.hasNext()) {
            Node node1 = it.next();
            if (node == node1)
                return true;
        }
        return false;
    }

    /* 此時棧中的節點組成一條所求路徑,轉儲並打印輸出 */
    public static void showAndSavePath() {
        Node[] o = stack.stream().toArray(Node[]::new);
        for (int i = 0; i < o.length; i++) {
            Node nNode = o[i];

            if (i < (o.length - 1))
                System.out.print(nNode.getName() + "->");
            else
                System.out.print(nNode.getName());
        }
        sers.add(o); /* 轉儲 */
        System.out.println("\n");
    }

    /*
     * 尋找路徑的方法
     * cNode: 當前的起始節點currentNode
     * pNode: 當前起始節點的上一節點previousNode
     * sNode: 最初的起始節點startNode
     * eNode: 終點endNode
     */
    public static boolean getPaths(Node cNode, Node pNode, Node sNode) {
        Node nNode = null;
        /* 如果符合條件判斷說明出現環路,不能再順着該路徑繼續尋路,返回false */
        if (cNode != null && pNode != null && cNode == pNode)
            return false;

        if (cNode != null) {
            int i = 0;
            /* 起始節點入棧 */
            stack.push(cNode);
            /* 如果該起始節點就是終點,說明找到一條路徑 */
            //這裏可以增加多個規則,比如不經過某些結點,某類結點標識出口,如果存在重複結點及路徑的時候
            //可以通過記錄已有路徑的首位結點來過濾重複路徑等。
            if (cNode.name.equalsIgnoreCase("node2")) return true;
            if (cNode.name.equalsIgnoreCase("node8")) {
                /* 轉儲並打印輸出該路徑,返回true */
                showAndSavePath();
                return true;
            }
            /* 如果不是,繼續尋路 */
            else {
                /*
                 * 從與當前起始節點cNode有連接關係的節點集中按順序遍歷得到一個節點
                 * 作爲下一次遞歸尋路時的起始節點
                 */
                nNode = cNode.getRelationNodes().get(i);
                while (nNode != null) {
                    /*
                     * 如果nNode是最初的起始節點或者nNode就是cNode的上一節點或者nNode已經在棧中 ,
                     * 說明產生環路 ,應重新在與當前起始節點有連接關係的節點集中尋找nNode
                     */
                    if (pNode != null && (nNode == sNode || nNode == pNode || isNodeInStack(nNode))) {
                        i++;
                        if (i >= cNode.getRelationNodes().size())
                            nNode = null;
                        else {
                            nNode = cNode.getRelationNodes().get(i);
                        }
                        continue;
                    }

                    /* 以nNode爲新的起始節點,當前起始節點cNode爲上一節點,遞歸調用尋路方法 */
                    if (getPaths(nNode, cNode, sNode))/* 遞歸調用 */ {
                        /* 如果找到一條路徑,則彈出棧頂節點 */
                        stack.pop();
                    }
                    /* 繼續在與cNode有連接關係的節點集中測試nNode */
                    i++;
                    if (i >= cNode.getRelationNodes().size())
                        nNode = null;
                    else
                        nNode = cNode.getRelationNodes().get(i);
                }
                /*
                 * 當遍歷完所有與cNode有連接關係的節點後,
                 * 說明在以cNode爲起始節點到終點的路徑已經全部找到
                 */
                stack.pop();
                return false;
            }
        } else
            return false;
    }

    public static void main(String[] args) {
        /* 定義節點數組 */
        Node[] node = new Node[11];

        for (int i = 0; i < node.length; i++) {
            node[i] = new Node();
            node[i].setName("node" + (i + 1));
            node[i].id = i;
        }

        node[0].setRelationNodes(Arrays.asList(node[3], node[1], node[10]));
        node[1].setRelationNodes(Arrays.asList(node[0], node[2], node[3]));
        node[2].setRelationNodes(Arrays.asList(node[1], node[4], node[10]));
        node[3].setRelationNodes(Arrays.asList(node[0], node[1], node[5], node[8], node[10]));
        node[4].setRelationNodes(Arrays.asList(node[2], node[8], node[9]));
        node[5].setRelationNodes(Arrays.asList(node[3], node[6]));
        node[6].setRelationNodes(Arrays.asList(node[5], node[7], node[8]));
        node[7].setRelationNodes(Arrays.asList(node[6], node[8]));
        node[8].setRelationNodes(Arrays.asList(node[1], node[3], node[6], node[7], node[4], node[10]));
        node[9].setRelationNodes(Arrays.asList(node[4], node[10]));
        node[10].setRelationNodes(Arrays.asList(node[9]));

        /* 開始搜索所有路徑 */
        getPaths(node[0], null, node[0]);
    }

    public static class Node {
        private Integer id;
        public String name = null;
        public List<Node> relationNodes = new ArrayList<Node>();

        public String getName() {
            return name;
        }

        public void setName(String name) {
            this.name = name;
        }

        public List<Node> getRelationNodes() {
            return relationNodes;
        }

        public void setRelationNodes(List<Node> relationNodes) {
            this.relationNodes = relationNodes;
        }
    }

}

以上算法參考無向圖如下:

[圖片]
在這裏插入圖片描述
計算的是從1開始,尋找出口爲8的路徑,路徑中不包含2結點。

結果如下

node1->node4->node6->node7->node8

node1->node4->node6->node7->node9->node8

node1->node4->node9->node7->node8

node1->node4->node9->node8

node1->node4->node11->node10->node5->node9->node7->node8

node1->node4->node11->node10->node5->node9->node8

node1->node11->node10->node5->node9->node4->node6->node7->node8

node1->node11->node10->node5->node9->node7->node8

node1->node11->node10->node5->node9->node8

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章