1 有向图的寻路
1.1 单点有向路径
单点有向路径问题:给定一幅有向图和一个起点s,从s到给定的目的顶点v是否存在一条有向路径?如果有找出这条路径。
算法和逻辑同无向图中处理一致,下面给出非递归实现代码如下:
package com.gaogzhen.datastructure.graph.directed;
import com.gaogzhen.datastructure.graph.undirected.Entry;
import com.gaogzhen.datastructure.stack.Stack;
import edu.princeton.cs.algs4.Digraph;
import java.util.Iterator;
/**
* @author Administrator
*/
public class DepthFirstDirectedPaths {
/**
* 标记数据
*/
private boolean[] marked;
/**
* 起点可达顶点到起点的路径
*/
private int[] edgeTo;
/**
* 起点
*/
private final int s;
/**
* 计算有向图G从给定起点s可达的路径
*
* @param G 有向图
* @param s 起点
* @throws IllegalArgumentException unless {@code 0 <= s < V}
*/
public DepthFirstDirectedPaths(Digraph G, int s) {
marked = new boolean[G.V()];
edgeTo = new int[G.V()];
this.s = s;
validateVertex(s);
dfs(G);
}
private void dfs(Digraph digraph) {
Stack<Entry<Integer, Iterator<Integer>>> path = new Stack<>();
// marked[v] = true;
if (!marked[s]) {
// 键值对起点-起点对应邻接表迭代器压入栈中
marked[s] = true;
Iterable<Integer> iterable = digraph.adj(s);
Iterator<Integer> it;
if (iterable != null && (it = iterable.iterator()) != null){
path.push(new Entry<>(s, it));
}
}
while (!path.isEmpty()) {
Entry<Integer, Iterator<Integer>> entry = path.pop();
int x;
Iterator<Integer> it = entry.getValue();
Integer f = entry.getKey();
while (it.hasNext()) {
// 当前顶点对应的邻接表迭代器还有元素,获取下一个元素
x = it.next();
if (!marked[x]) {
// 顶点未被标记,标记顶点且标记路径x->f
// f是x所在邻接表对应的顶点
marked[x] = true;
edgeTo[x] = f;
if (it.hasNext()) {
// 邻接表迭代器还有元素,重新压入栈
path.push(entry);
}
// 按照深度优先原则,把新标记顶点对应的键值对压入栈中,在下次循环时优先访问
Iterable<Integer> iterable = digraph.adj(x);
if (iterable != null && (it = iterable.iterator()) != null){
path.push(new Entry<>(x, it));
}
break;
}
}
}
}
/**
* 从起点s到给定的目的顶点v是否存在一条有向路径
*
* @param v 给定的顶点
* @return {@code true} 存在一条从起点到顶点v的有向路径, {@code false} 不存在
* @throws IllegalArgumentException unless {@code 0 <= v < V}
*/
public boolean hasPathTo(int v) {
validateVertex(v);
return marked[v];
}
/**
* 从起点到给定顶点路径集合
*
* @param v 给定顶点
* @return 从起点到给定顶点路径集合
* @throws IllegalArgumentException unless {@code 0 <= v < V}
*/
public Iterable<Integer> pathTo(int v) {
validateVertex(v);
if (!hasPathTo(v)) {
return null;
}
Stack<Integer> path = new Stack<Integer>();
for (int x = v; x != s; x = edgeTo[x]) {
path.push(x);
}
path.push(s);
return path;
}
/**
* 校验顶点索引v是否合理
*
* @param v 顶点v
*/
private void validateVertex(int v) {
int V = marked.length;
if (v < 0 || v >= V) {
throw new IllegalArgumentException("vertex " + v + " is not between 0 and " + (V - 1));
}
}
}
测试代码:
public static void testDepthFirstDirectedPaths() {
String path = "H:\\gaogzhen\\java\\projects\\algorithm\\asserts\\tinyDG.txt";
In in = new In(path);
Digraph digraph = new Digraph(in);
int s = 0;
DepthFirstDirectedPaths directedPaths = new DepthFirstDirectedPaths(digraph, s);
int v = 2;
System.out.println(directedPaths.hasPathTo(v));
System.out.println(directedPaths.pathTo(v));
}
测试结果:
true
[0, 5, 4, 3, 2]
执行流程、算法逻辑和API参考之前文章0104路径搜索和单点路径-无向图-数据结构和算法(Java)
1.2 单点最短有向路径
单点最短有向路径问题:给定一幅图和一个起点s,从s到给定的目的顶点v是否存在一条有向路径?如果有,找出其中最短的那条(所含边数量最少)。
非递归实现:
package com.gaogzhen.datastructure.graph.directed;
import com.gaogzhen.datastructure.stack.Stack;
import edu.princeton.cs.algs4.Digraph;
import edu.princeton.cs.algs4.Queue;
/**
* 单点最短有向路径
* @author Administrator
*/
public class BreadthFirstDirectedPaths {
private static final int INFINITY = Integer.MAX_VALUE;
/**
* 标记
*/
private boolean[] marked;
/**
* 起点可达顶点到起点的路径
*/
private int[] edgeTo;
/**
* 到起点有向路径的长度
*/
private int[] distTo;
/**
* 计算有向图G中起点s可达路径
* @param G 有向图
* @param s 起点
* @throws IllegalArgumentException unless {@code 0 <= v < V}
*/
public BreadthFirstDirectedPaths(Digraph G, int s) {
marked = new boolean[G.V()];
distTo = new int[G.V()];
edgeTo = new int[G.V()];
for (int v = 0; v < G.V(); v++) {
distTo[v] = INFINITY;
}
validateVertex(s);
bfs(G, s);
}
/**
* 计算有向图G中多起点sources可达路径
* @param G 有向图
* @param sources 多起点集合
* @throws IllegalArgumentException if {@code sources} is {@code null}
* @throws IllegalArgumentException unless each vertex {@code v} in
* {@code sources} satisfies {@code 0 <= v < V}
*/
public BreadthFirstDirectedPaths(Digraph G, Iterable<Integer> sources) {
marked = new boolean[G.V()];
distTo = new int[G.V()];
edgeTo = new int[G.V()];
for (int v = 0; v < G.V(); v++) {
distTo[v] = INFINITY;
}
validateVertices(sources);
bfs(G, sources);
}
/**
* 广度优先搜索有向图G从起点s可达的最短路径
* @param G 有向图
* @param s 起点
*/
private void bfs(Digraph G, int s) {
Queue<Integer> q = new Queue<Integer>();
marked[s] = true;
distTo[s] = 0;
q.enqueue(s);
while (!q.isEmpty()) {
int v = q.dequeue();
for (int w : G.adj(v)) {
if (!marked[w]) {
edgeTo[w] = v;
// 路径长度=父结点路径长度+1
distTo[w] = distTo[v] + 1;
marked[w] = true;
q.enqueue(w);
}
}
}
}
// BFS from multiple sources
private void bfs(Digraph G, Iterable<Integer> sources) {
Queue<Integer> q = new Queue<Integer>();
for (int s : sources) {
marked[s] = true;
distTo[s] = 0;
q.enqueue(s);
}
while (!q.isEmpty()) {
int v = q.dequeue();
for (int w : G.adj(v)) {
if (!marked[w]) {
edgeTo[w] = v;
distTo[w] = distTo[v] + 1;
marked[w] = true;
q.enqueue(w);
}
}
}
}
/**
* 是否存在一条从起点到给定目的顶点v的有向路径
* @param v 目的顶点
* @return {@code true} 存在一条有向路径, {@code false} 不存在
* @throws IllegalArgumentException unless {@code 0 <= v < V}
*/
public boolean hasPathTo(int v) {
validateVertex(v);
return marked[v];
}
/**
* 从起点到给定目的顶点最短路径长度
* @param v 目的顶点
* @return 从起点到给定目的顶点最短路径长度
* @throws IllegalArgumentException unless {@code 0 <= v < V}
*/
public int distTo(int v) {
validateVertex(v);
return distTo[v];
}
/**
* 从起点到给定目的顶点的最短有向路径
* @param v 目的顶点
* @return 起点到给定目的顶点的最短有向路径;没有返回{@code null}
* @throws IllegalArgumentException unless {@code 0 <= v < V}
*/
public Iterable<Integer> pathTo(int v) {
validateVertex(v);
if (!hasPathTo(v)) {
return null;
}
Stack<Integer> path = new Stack<Integer>();
int x;
for (x = v; distTo[x] != 0; x = edgeTo[x]) {
path.push(x);
}
path.push(x);
return path;
}
/**
* 校验顶点索引
* @param v 顶点索引
*/
private void validateVertex(int v) {
int V = marked.length;
if (v < 0 || v >= V) {
throw new IllegalArgumentException("vertex " + v + " is not between 0 and " + (V-1));
}
}
/**
* 校验顶点索引集合
* @param vertices 顶点索引结合
*/
private void validateVertices(Iterable<Integer> vertices) {
if (vertices == null) {
throw new IllegalArgumentException("argument is null");
}
int V = marked.length;
int count = 0;
for (Integer v : vertices) {
count++;
if (v == null) {
throw new IllegalArgumentException("vertex is null");
}
validateVertex(v);
}
if (count == 0) {
throw new IllegalArgumentException("zero vertices");
}
}
}
测试代码:
public static void testBreadthFirstDirectedPaths() {
String path = "H:\\gaogzhen\\java\\projects\\algorithm\\asserts\\tinyDG.txt";
In in = new In(path);
Digraph digraph = new Digraph(in);
// List<Integer> integers = Arrays.asList(0, 2);
BreadthFirstDirectedPaths paths = new BreadthFirstDirectedPaths(digraph, 0);
int v = 2;
System.out.println(paths.distTo(v));
System.out.println(paths.pathTo(v));
}
测试结果:
3
[0, 5, 4, 2]
执行流程、算法逻辑和API参考之前文章-0106广度优先搜索和最短路径-无向图-数据结构和算法(Java)
后记
如果小伙伴什么问题或者指教,欢迎交流。
❓QQ:806797785
⭐️源代码仓库地址:https://gitee.com/gaogzhen/algorithm
参考链接:
[1][美]Robert Sedgewich,[美]Kevin Wayne著;谢路云译.算法:第4版[M].北京:人民邮电出版社,2012.10.p368-369.