小橙书阅读指南(十二)——无向图、深度优先搜索和路径查找算法

在计算机应用中,咱们把一系列相链接的节点组成的数据结构,叫作图。今天咱们将要介绍它的一种形式——无向图,以及针对这种结构的深度优先搜索和路径查找算法。git

1、无向图数据结构算法

接口:数组

/**
 * 图论接口
 */
public interface Graph {
    /**
     * 顶点数
     *
     * @return
     */
    int vertexNum();

    /**
     * 边数
     *
     * @return
     */
    int edgeNum();

    /**
     * 向图中添加一条v-w的边
     *
     * @param v
     * @param w
     */
    void addEdge(int v, int w);

    /**
     * 和v相邻的全部顶点
     *
     * @param v
     * @return
     */
    Iterable<Integer> adjoin(int v);

    /**
     * v的维度
     *
     * @param v
     * @return
     */
    int degree(int v);
}

实现类:数据结构

public class Graph implements algorithms.graphs.ifs.Graph {
    private final int vertex; // 顶点
    private int edge; //
    private ArrayList<Integer>[] adj;

    public Graph(int v) {
        this.vertex = v;
        this.edge = 0;
        adj = (ArrayList<Integer>[]) new ArrayList[v];
        for (int i = 0; i < v; i++) {
            adj[i] = new ArrayList<>();
        }
    }

    @Override
    public int vertexNum() {
        return vertex;
    }

    @Override
    public int edgeNum() {
        return edge;
    }

    @Override
    public void addEdge(int v, int w) {
        validateVertex(v);
        validateVertex(w);
        adj[v].add(w);
        adj[w].add(v);
        edge++;
    }

    @Override
    public Iterable<Integer> adjoin(int v) {
        return adj[v];
    }

    @Override
    public int degree(int v) {
        return adj[v].size();
    }

    private void validateVertex(int v) {
        if (v < 0 || v > this.vertex) {
            throw new IllegalArgumentException();
        }
    }
}

2、深度搜索优先算法less

对于图的处理咱们经常经过系统地检查每个顶点和每一条边来获取图的各类性质。对于图的问题咱们最常常被问及的是:a点和b点连通吗?若是连通如何到达?为了描述方便,咱们使用天然数描述图的每个顶点。ide

假设有如下图的结构函数

 左侧数组表示节点,右侧表明与节点链接的其余节点。该结构的标准画法以下:this

算法描述:深度优先搜索从起点出发(0)遍历(2,1,5)并递归(2)与它连接的点,被搜索到的点将不会被再次递归直到全部的点都被搜索到为止。spa

深度优先搜索接口与实现:code

// 接口
public interface Search {
    boolean marked(int v);
    int count();
}

/**
 * 图论:深度优先搜索
 */
public class DepthFirstSearch implements Search {
    private boolean[] marked;
    private int count;

    public DepthFirstSearch(Graph g, int s) {
        marked = new boolean[g.vertexNum()];
        validateVertex(s);
        dfs(g, s);
    }

    /**
     * 以递归的方式从s起点出发,标记每个通过的顶点,未被标记的顶点为不连通
     *
     * @param g
     * @param v
     */
    private void dfs(Graph g, int v) {
        marked[v] = true;
        count++;
        for (int x : g.adjoin(v)) {
            if (!marked[x]) {
                dfs(g, x);
            }
        }
    }

    @Override
    public boolean marked(int v) {
        validateVertex(v);
        return marked[v];
    }

    @Override
    public int count() {
        return count;
    }

    // throw an IllegalArgumentException unless {@code 0 <= vertexNum < V}
    private void validateVertex(int v) {
        int V = marked.length;
        if (v < 0 || v >= V)
            throw new IllegalArgumentException();
    }

}

这套算法的核心是dfs函数。咱们要理解深度优先算法就必须弄清楚算法递归的过程。marked数组记录节点的访问状况,变量x和v的递归过程以下:

标准画法:

 深度优先算法按照上面的路径搜索图,由此咱们能够获知深度搜索算法的两个特征:

1.搜索路径沿一条路径向下扩展,每个节点只会被遍历一次(每个节点均可以知道在搜索路径上的上一个节点,并惟一肯定)。

2.搜索路径上的任意两点表明可达,但并不是最短路径。

这样咱们就能够回答本文最先提出的有关图的第一个问题:a点和b点连通吗?显然,以a为起点搜索整个图,若是b点在路径上则表示连通。

3、使用深度优先搜索的路径算法

要回答有关图的第二个问题:若是连通如何到达?回忆上一段咱们总结的深度搜索算法的第一个特征,咱们可使用数组结构在存储每个节点的上一个节点。

路径搜索接口和实现:

/**
 * 寻找路径
 */
public interface Paths {
    boolean hasPathTo(int vertex);

    Iterable<Integer> pathTo(int vertex);
}

/**
 * 基于深度优先搜索的路径搜索算法
 */
public class DepthFirstPaths implements Paths {
    private final int s;
    private boolean[] marked;
    private int[] edgeTo;

    public DepthFirstPaths(Graph g, int s) {
        this.s = s;
        marked = new boolean[g.vertexNum()];
        edgeTo = new int[g.vertexNum()];
        dfs(g, s);
    }

    private void dfs(Graph g, int v) {
        marked[v] = true;
        for (int w : g.adjoin(v)) {
            if (!marked[w]) {
                edgeTo[w] = v;
                dfs(g, w);
            }
        }
    }

    @Override
    public boolean hasPathTo(int vertex) {
        return marked[vertex];
    }

    @Override
    public Iterable<Integer> pathTo(int vertex) {
        if (!hasPathTo(vertex)) {
            return null;
        }
        Stack<Integer> path = new Stack<>();
        for (int i = vertex; i != s; i = edgeTo[i]) {
            path.push(i);
        }
        path.push(s);
        return path;
    }
}

 算法的标准画法:

相关连接:

Algorithms for Java

相关文章
相关标签/搜索