图的广度优先遍历和深度优先遍历(连通图和非连通图)

2019独角兽企业重金招聘Python工程师标准>>> hot3.png

图遍历的定义:

从图中某个顶点出发,访问图中其余顶点,并且是图中每个顶点仅被访问一次。包括2中,深度优先遍历(DFS)和广度优先遍历(BFS)。

 

准备工作,我们需要对之前的java代码进行一些补充,来满足遍历算法的实现。

public class Graph {
    public List nodes = new ArrayList();
    public int[][] arcs;

    public static void main(String[] args){
        Graph graph = new Graph();
        String nodeNames[] = {"aaa", "bbb", "ccc", "ddd"};
        for (String s : nodeNames){
            graph.nodes.add(new Node(s));
        }

        graph.arcs = new int[nodeNames.length][nodeNames.length];

        for (int i = 0; i < graph.nodes.size(); i ++){
            for (int j = 0; j < graph.nodes.size(); j ++){
                graph.arcs[i][j] = i == j ? 0 : 1;
            }
        }
    }
}

public class Node {
    private String nodeName;

    public Node(String name){
        this.nodeName = name;
    }

    private boolean visitFlag = false;
    public void visit(){
        if (visitFlag){
            throw new RuntimeException("This node has been visited!");
        }else{
            System.out.println(nodeName);
            visitFlag = true;
        }
    }
    public boolean isVisited(){
        return visitFlag;
    }
}

连通图的深度优先遍历

先说算法,访问V0,找到它的第一个未访问邻接节点V1,访问V1,找到它的第一个未访问邻接节点V2,访问V2,以此类推,如果某个结点的所有邻接节点都已访问,找到最后一个访问节点,继续找它的第一个未访问邻接节点,直到所有节点访问完毕。

代码的解释,首先定义一个当前序号curIndex = 0,然后定义一个已访问node的index栈,还有已访问结点个数。循环当中,首先访问当前节点,之后,找它的第一个未访问邻接节点,如果找到的话,将当前index进栈,找到的这个节点作为当前节点,继续循环,如果没找到,需要从已访问节点栈当中拿最顶端的出来,继续找它的第一个未访问邻接节点。如果已访问节点数量==总结点数量,意味着遍历结束。还有一个问题,我们这个算法只适用于连通图,如果是非连通图,会报错“The graph is not all connected!”。

    public void dfs() {
        int curIndex = 0;
        Stack preindexes = new Stack();
        int visitedCnt = 0;
        while (true) {
            Node curNode = nodes.get(curIndex);
            if (!curNode.isVisited()) {
                curNode.visit();
                visitedCnt ++;
                if (visitedCnt == nodes.size()){
                    break;
                }
            }

            LoopForFound: while(true){
                for (int i = 0; i < arcs[curIndex].length; i++) {
                    if (arcs[curIndex][i] == 1 && !nodes.get(i).isVisited()) {
                        preindexes.push(curIndex);
                        curIndex = i;
                        break LoopForFound;
                    }
                }
                if (preindexes.isEmpty()){
                    throw new RuntimeException("The graph is not all connected!");
                }
                curIndex = preindexes.pop();
            }
        }
    }


   

连通图的广度优先遍历

先说算法,访问V,找到它的所有未访问邻接节点,全部访问一遍,然后拿到它的第一个邻接节点V1,找到V1的所有未访问邻接节点,全部访问一遍,在拿到V0的第二个邻接节点V2,以此类推,直到所有节点访问完毕。

代码的解释,curIndex 表示当前访问节点序号,然后是已访问节点队列visitedQueue ,已访问节点个数,循环的开始,有一个特殊的判断,如果是第一次进来,直接把curIndex 设为0,访问,并进入队列,判断已访问个数;如果不是第一次,直接从队列当中取一个出来作为当前序号;然后查找并访问当前节点的所有未访问邻接节点, 并进入队列,然后判断已访问个数。还有一个问题,我们这个算法只适用于连通图,如果是非连通图,会报错“The graph is not all connected!”。

public void bfs() {
        int curIndex = -1;
        Queue visitedQueue = new LinkedBlockingQueue<>();
        int visitedCnt = 0;
        BFSTopLoop: while(true){
            if (curIndex == -1){
                curIndex = 0;
                nodes.get(curIndex).visit();
                visitedQueue.offer(curIndex);
                visitedCnt ++;
                if (visitedCnt == nodes.size()){
                    break BFSTopLoop;
                }
            }else{
                if (visitedQueue.isEmpty()){
                    throw new RuntimeException("The graph is not all connected!");
                }
                curIndex = visitedQueue.poll();
            }

            for (int i = 0; i < arcs[curIndex].length; i ++){
                if (arcs[curIndex][i] == 1){
                    Node tmp = nodes.get(i);
                    if (!tmp.isVisited()){
                        tmp.visit();
                        visitedQueue.offer(i);
                        visitedCnt ++;
                        if (visitedCnt == nodes.size()){
                            break BFSTopLoop;
                        }
                    }
                }
            }
        }
    }

上面2个遍历算法,都是针对连通图的实现,如果要适应非连通图,需要做下面几点修改

1、把while(true)的外面加上对所有节点的循环

2、抛出异常的部分改成continue最外层循环

看一下代码,首先是非连通图的深度优先遍历,红色部分是针对之前的算法修改的部分。

public void dfsForNoAllConnected() {
        int visitedCnt = 0;
        TOPLOOP:
        for (int curIndex = 0; curIndex < nodes.size(); curIndex++) {

            if (nodes.get(curIndex).isVisited()){
                continue;
            }

            Stack preindexes = new Stack();
            while (true) {
                Node curNode = nodes.get(curIndex);
                if (!curNode.isVisited()) {
                    curNode.visit();
                    visitedCnt++;
                    if (visitedCnt == nodes.size()) {
                        break TOPLOOP;
                    }
                }

                LoopForFound:
                while (true) {
                    for (int i = 0; i < arcs[curIndex].length; i++) {
                        if (arcs[curIndex][i] == 1 && !nodes.get(i).isVisited()) {
                            preindexes.push(curIndex);
                            curIndex = i;
                            break LoopForFound;
                        }
                    }
                    if (preindexes.isEmpty()) {
                        continue TOPLOOP;
                    }
                    curIndex = preindexes.pop();
                }
            }
        }
    }

然后是非连通图的广度度优先遍历

public void bfsForNoAllConnected() {
        int visitedCnt = 0;
        TOPLOOP:
        for (int curIndex = 0; curIndex < nodes.size(); curIndex++) {

            if (nodes.get(curIndex).isVisited()){
                continue;
            }
            Queue visitedQueue = new LinkedBlockingQueue<>();
            boolean firstLoop = true;

            BFSTopLoop:
            while (true) {
                if (firstLoop) {
                    nodes.get(curIndex).visit();
                    visitedQueue.offer(curIndex);
                    visitedCnt++;
                    if (visitedCnt == nodes.size()) {
                        break TOPLOOP;
                    }
                    firstLoop = false;
                } else {
                    if (visitedQueue.isEmpty()) {
                        continue TOPLOOP;
                    }
                    curIndex = visitedQueue.poll();
                }

                for (int i = 0; i < arcs[curIndex].length; i++) {
                    if (arcs[curIndex][i] == 1) {
                        Node tmp = nodes.get(i);
                        if (!tmp.isVisited()) {
                            tmp.visit();
                            visitedQueue.offer(i);
                            visitedCnt++;
                            if (visitedCnt == nodes.size()) {
                                break TOPLOOP;
                            }
                        }
                    }
                }
            }
        }
    }

转载于:https://my.oschina.net/dongtianxi/blog/796703

你可能感兴趣的:(图的广度优先遍历和深度优先遍历(连通图和非连通图))