图的DFS和BFS实现

230 阅读1分钟

图的基本实现

/**
 * 顶点集合
 */
private char[] vertex;
/**
 * 邻接矩阵
 */
private int[][] matrix;
/**
 * 最大值,表示两个顶点之间不连通
 */
private static final int INF = Integer.MAX_VALUE;

/**
 * 用提供的顶点和邻接矩阵创建图
 *
 * @param vertex
 * @param matrix
 */
public Graph(char[] vertex, int[][] matrix) {
    this.vertex = vertex;
    this.matrix = matrix;
}
/**
 * 返回顶点vertex的第一个邻接顶点的索引,失败返回-1
 *
 * @param vertex
 * @return
 */
public int firstVertex(int vertex) {
    if (vertex < 0 || vertex > (this.vertex.length - 1)) {
        return -1;
    }
    for (int i = 0; i < this.vertex.length; i++) {
        if (this.matrix[vertex][i] != 0 && this.matrix[vertex][i] != INF) {
            return i;
        }
    }
    return -1;
}

/**
 * 返回顶点vertex相对于它的一个邻接顶点index的下一个邻接顶点索引
 *
 * @param vertex
 * @return
 */
public int nextVertex(int vertex, int index) {
    if (vertex < 0 || vertex > (this.vertex.length - 1) || index < 0 || index > (this.vertex.length - 1)) {
        return -1;
    }
    for (int i = index + 1; i < this.vertex.length; i++) {
        if (this.matrix[vertex][i] != 0 && this.matrix[vertex][i] != INF) {
            return i;
        }
    }
    return -1;
}

深度优先遍历DFS

思想

类似于树的前序遍历,采用的搜索方法的特点是尽可能先对纵深方向进行搜索。 首先访问出发点v,并将其标记为已访问过;然后依次从v出发搜索v的每个邻接点w。若w未曾访问过,则以w为新的出发点继续进行深度优先遍历,直至图中所有和源点v有路径相通的顶点(亦称为从源点可达的顶点)均已被访问为止。若此时图中仍有未访问的顶点,则另选一个尚未访问的顶点作为新的源点重复上述过程,直至图中所有顶点均已被访问为止,这里定义使用的就是递归定义。

递归实现

以纵向优先的方式遍历节点。我们从当前节点curr出发,如果当前节点被访问过,就返回,否则将该节点标记为访问过的节点,然后在递归访问当前节点的所有邻接节点。

/**
 * DFS:深度优先遍历递归版
 *
 * @param i
 * @param visited
 */
public void DFS(int i, boolean[] visited) {
    //标记当前顶点以访问
    visited[i] = true;
    System.out.print(vertex[i] + "-->");
    //遍历该顶点的邻接顶点,若是没有访问过,继续往下遍历
    for (int index = firstVertex(i); index >= 0; index = nextVertex(i, index)) {
        if (!visited[index]) {
            DFS(index, visited);
        }
    }
}

非递归实现(栈和回溯法)

首先我们需要一个栈结构来存放需要被访问的节点,当然栈的第一个元素是我们传入的node节点,如果这个栈里面还有节点的话,拿出栈顶节点,访问该节点,然后将这个节点的所有未被访问的邻接节点压入栈中。

/**
 * DFS:深度优先遍历非递归版
 */
public void DFS() {
    Stack<Integer> stack = new Stack<>();
    boolean[] visited = new boolean[this.vertex.length];
    //开始顶点入栈,标记已访问
    stack.push(0);
    visited[0] = true;
    while (!stack.isEmpty()) {
        int current = stack.pop();
        System.out.print(vertex[current] + "-->");
        for (int i = 0; i < this.vertex.length; i++) {
            if (!visited[i] && matrix[current][i] != INF && matrix[current][i] != 0) {
                visited[i] = true;
                stack.push(i);
                break;
            }
        }

    }
}

广度优先遍历BFS

思想

广度优先遍历是连通图的一种遍历策略,它的思想是从一个顶点V0开始,辐射状地优先遍历其周围较广的区域。也就是从V0出发,访问V0的各个未曾访问的邻接点W1,W2,…,Wk;然后,依次从W1,W2,…,Wk出发访问各自未被访问的邻接点。

算法步骤

广度优先指的是从当前节点curr出发,将该节点标记为已经访问过的节点,然后在依次访问curr的没有被访问的邻接节点,然后在依次访问邻接节点的邻接节点,直到所有的节点被访问。

代码实现

/**
 * 广度优先遍历算法 Breadth-first search(非递归)
 */
public void BFS() {
    // LinkedList实现了Queue接口 FIFO
    Queue<Integer> queue = new LinkedList<Integer>();
    boolean[] visited=new boolean[vertex.length];
    for (int i = 0; i < vertex.length; i++) {
        visited[i] = false;
    }

    //这个循环是为了确保每个顶点都被遍历到
    for (int i = 0; i < vertex.length; i++) {
        if (!visited[i]) {
            queue.add(i);
            visited[i] = true;
            System.out.print(vertex[i] + "-->");
            while (!queue.isEmpty()) {
                int row = queue.remove();
                for (int k = firstVertex(row); k >= 0; k = nextVertex(row, k)) {
                    if (!visited[k]) {
                        queue.add(k);
                        visited[k] = true;
                        System.out.print(vertex[k] + "-->");
                    }
                }

            }
        }
    }
}