【算法】无向图中的环

无向图中的环

判断一个具有n个结点m条边的无向图中是否包含环,如果包含则输出其中一个环,要求时间复杂度为O(m+n)。可以根据对图的深度优先算法(DSF)拓展来求解此题。主要过程如下:

  • 用数组VST[]记录图中访问过的结点(如 VST[i] 表示第i个结点已经被访问)
  • 用PRE[]记录当前结点的父结点的位置(如 PRE[i]=j 表示i个结点的父结点的位置是j)
  • 通过DFS来搜索图中的结点,当搜索过程中当前结点的下一结点时此前已被访问的结点时,停止搜索,从该结点开始通过PRE[]数组对环的路径进行回溯

题目要求算法的时间复杂度为O(n+m),因为实现过程中庶采用邻接表的方式表示图,以下是使用Java实现的算法主要内容:

邻接表数据结构的定义

// 表头结点
public class VertexNode {
    public Integer id;			//结点序号
    public String data; 		// 结点信息
    public EdgeNode firstEdge;  // 第一条边
    
    // 构造函数及Setter Getter省略
}

// 边表结点
public class EdgeNode {
    public Integer adjvex; 		// 每条边的下一结点
    public EdgeNode next;  		// 下一个边结点
    
    // 构造函数及Setter Getter省略
}

// 邻接表表示的图
public class Graph {
    public Integer numVertex; 		// 图中结点数量
    public VertexNode[] vertexNodes;// 邻接表中表头结点集合
    public boolean[] vst;			// 结点访问标记
    public Integer[] pre;			// 父结点记录数组,用于回溯,初始值为-1——表示没有父结点
    public boolean ringFinded;		// 标记是否已经找到环
    
    // 构造函数及Setter Getter省略
}

算法核心

// 通过DFS找环
public void findRing(int root) {
    vst[root] = true; // 标记为已访问

    VertexNode vertexNode = vertexNodes[root]; 		 // 当前表头结点
    EdgeNode currentEdgeNode = vertexNode.firstEdge; 

    while (currentEdgeNode != null && ringFinded == false) { // 遍历边结点,当找到环时结束
        int vertexNodeIndex = currentEdgeNode.adjvex;		 

        if (vst[vertexNodeIndex] == false){
            pre[vertexNodeIndex] = root; 	// 记录父结点
            findRing(vertexNodeIndex);				// 递归搜索子结点
        }else if (pre[root] != vertexNodeIndex){
            ringFinded = true;
            backPath(root);
            break;
        }

        currentEdgeNode = currentEdgeNode.next; 	// 搜索下一表头结点
    }
}

// 回溯路径
public void backPath(int index){
    while (index != -1){
        System.out.println(vertexNodes[index].data);
        index = pre[index];
    }
}

完整代码


public class GraphDFS {
    public Integer numVertex;
    public Integer numEdge;
    public VertexNode[] vertexNodes;
    public boolean[] vst;
    public Integer[] pre;
    public boolean ringFinded;
    public Integer[] numLayerNode;
    public Integer[] nodeLyaer;

    public GraphDFS(VertexNode[] vertexNodes) {
        this.numEdge = 0;
        this.numVertex = vertexNodes.length;
        this.vertexNodes = vertexNodes;
        vst = new boolean[numVertex];
        pre = new Integer[numVertex];

        for (int i = 0; i < pre.length; i++) {
            pre[i] = -1;
        }
    }

    public void insertEdge(Integer start, Integer end) {
        VertexNode vertexNode = vertexNodes[start];
        EdgeNode edgeNode = new EdgeNode(end);

        EdgeNode firstEdgeNode = vertexNode.firstEdge;
        if (firstEdgeNode == null) {
            vertexNode.firstEdge = edgeNode;
        } else {
            edgeNode.next = firstEdgeNode;
            vertexNode.firstEdge = edgeNode;
        }
    }

    public void dfs(int root){
        VertexNode vertexNode = this.vertexNodes[root];
        vst[root] = true;
        System.out.print(vertexNode.data + " ");

        EdgeNode currentEdgeNode = vertexNode.firstEdge;

        while (currentEdgeNode != null) {
            int vertexNodeIndex = currentEdgeNode.adjvex;
            if (vst[vertexNodeIndex] == false){
                dfs(vertexNodeIndex);
            }
            currentEdgeNode = currentEdgeNode.next;
        }
    }

    // 通过DFS找环
    public void findRing(int root) {
        vst[root] = true; // 标记为已访问

        VertexNode vertexNode = vertexNodes[root]; 		 // 当前表头结点
        EdgeNode currentEdgeNode = vertexNode.firstEdge;

        while (currentEdgeNode != null && ringFinded == false) { // 遍历边结点,当找到环时结束
            int vertexNodeIndex = currentEdgeNode.adjvex;

            if (vst[vertexNodeIndex] == false){
                pre[vertexNodeIndex] = root; 	// 记录父结点
                findRing(vertexNodeIndex);				// 递归搜索子结点
            }else if (pre[root] != vertexNodeIndex){
                ringFinded = true;
                backPath(root);
                break;
            }

            currentEdgeNode = currentEdgeNode.next; 	// 搜索下一表头结点
        }
    }

    // 回溯路径
    public void backPath(int index){
        while (index != -1){
            System.out.println(vertexNodes[index].data);
            index = pre[index];
        }
    }

    public static void main(String[] args) {
        VertexNode[] vertexNodes = {
                new VertexNode(1,"a"),
                new VertexNode(2,"b"),
                new VertexNode(3,"c"),
                new VertexNode(4,"d"),
                new VertexNode(5,"e"),
                new VertexNode(6,"f")
        };

        GraphDFS graph = new GraphDFS(vertexNodes);

        graph.insertEdge(0, 2);
        graph.insertEdge(0, 1);
        graph.insertEdge(1, 4);
        graph.insertEdge(1, 3);
        graph.insertEdge(1, 0);
        graph.insertEdge(2, 5);
        graph.insertEdge(2, 4);
        graph.insertEdge(2, 0);
        graph.insertEdge(3, 1);
        graph.insertEdge(4, 2);
        graph.insertEdge(4, 1);
        graph.insertEdge(5, 2);

        graph.findRing(0);

//        打印邻接表结构
//        for (int i = 0; i < graph.numVertex; i++) {
//            VertexNode vertexNode = graph.vertexNodes[i];
//            EdgeNode firstEdge = vertexNode.firstEdge;
//
//            EdgeNode currentEdge = firstEdge;
//            System.out.print(vertexNode.data + ":");
//            while (currentEdge != null) {
//                int vertexNodeIndex = currentEdge.adjvex;
//                System.out.print("->" + vertexNodes[vertexNodeIndex].data);
//                currentEdge = currentEdge.next;
//            }
//            System.out.println();
//        }
    }
}

你可能感兴趣的:(算法,Java)