吐血整理程序员必读书单:https://github.com/silently9527/ProgrammerBooksgit
微信公众号:贝塔学Java程序员
在前面两篇中咱们经过深度优先搜索能够从图中找出一条经过顶点v到顶点w的路径,可是深度优先搜索与顶点的输入有很大的关系,找出来的路径也不必定是最短的,一般状况下咱们不少时候须要找出图中的最短路径,好比:地图功能。这里咱们就须要使用到广度优先搜索算法github
依然使用以前定义的寻找路径的API算法
public class Paths { Paths(Graph graph, int s); boolean hasPathTo(int v); //判断出从s->v是否存在路径 Iterable<Integer> pathTo(int v); //若是存在路径,返回路径 }
在广度优先搜索中,为了找出最短路径,咱们须要按照起点的顺序来遍历全部的顶点,而不在是使用递归来实现;算法的思路:数据库
在该算法中,为了保存路径,咱们依然须要使用一个边的数组edgeTo[],用一颗父链树来表示根节点到全部连通顶点的最短路径。数组
public class BreadthFirstPaths { private boolean marked[]; private int[] edgeTo; private int s; private Queue<Integer> queue = new LinkedListQueue<>(); public BreadthFirstPaths(Graph graph, int s) { this.s = s; this.marked = new boolean[graph.V()]; this.edgeTo = new int[graph.V()]; bfs(graph, s); } private void bfs(Graph graph, int s) { this.marked[s] = true; this.queue.enqueue(s); while (!this.queue.isEmpty()) { Integer v = this.queue.dequeue(); for (int w : graph.adj(v)) { if (!this.marked[w]) { this.marked[w] = true; this.edgeTo[w] = v; this.queue.enqueue(w); } } } } public boolean hasPathTo(int v) { return this.marked[v]; } public Iterable<Integer> pathTo(int v) { if (!hasPathTo(v)) { throw new IllegalStateException("s不能到达v"); } Stack<Integer> stack = new LinkedListStack<>(); stack.push(v); while (edgeTo[v] != s) { stack.push(edgeTo[v]); v = edgeTo[v]; } stack.push(s); return stack; } }
如下图为列,来看看广度优先搜索的运行轨迹微信
单元测试的代码:单元测试
@Test public void test() { Graph graph = new Graph(8); graph.addEdge(0, 1); graph.addEdge(0, 2); graph.addEdge(0, 5); graph.addEdge(1, 3); graph.addEdge(2, 4); graph.addEdge(4, 3); graph.addEdge(5, 3); graph.addEdge(6, 7); BreadthFirstPaths paths = new BreadthFirstPaths(graph, 0); System.out.println(paths.hasPathTo(5)); System.out.println(paths.hasPathTo(2)); System.out.println(paths.hasPathTo(6)); paths.pathTo(5).forEach(System.out::print); System.out.println(); paths.pathTo(4).forEach(System.out::print); System.out.println(); paths.pathTo(2).forEach(System.out::print); System.out.println(); paths.pathTo(3).forEach(System.out::print); }
执行的结果与咱们分析的运行轨迹一致学习
最近几篇文章一块儿学习到的图算法都是以数字做为了顶点,是由于数字来实现这些算法会很是的简洁方便,可是在实际的场景中,一般都是使用的字符做为顶点而非数字,好比:地图上的位置、电影与演员的关系。测试
为了知足实际的场景,咱们只须要在数字与字符串的关系作一个映射,此时咱们会想到以前文章实现的map(经过二叉树实现map、红黑树实现map、哈希表实现map等等,有兴趣的同窗能够去翻翻),使用Map来维护字符串和数字的映射关系。
public interface SymbolGraph { boolean contains(String key); //判断是否存在顶点 int index(String key); //经过名称返回对应的数字顶点 String name(int v); //经过数字顶点返回对应的字符名称 Graph graph(); }
实现的思路:
假设构造图的顶点与边是经过字符串来表示的,如:a,b,c,d\nb,a,h,l,p\ng,s,z
使用\n分隔的每段第一个字符串表示顶点v,后面的表示与顶点v相连的相邻顶点;
实际的过程能够根据具体状况来肯定,不必定非得这种字符串,能够来源于数据库,也能够来源于网路的请求。
代码实现以下:
public class SymbolGraph { private Map<String, Integer> map = new RedBlack23RedBlackTreeMap<>(); private String[] keys; private Graph graph; public SymbolGraph(String text) { Arrays.stream(text.split("\n")).forEach(line -> { String[] split = line.split(","); for (int i = 0; i < split.length; i++) { map.put(split[i], i); } }); this.keys = new String[map.size()]; map.keys().forEach(key -> { this.keys[this.map.get(key)] = key; }); this.graph = new Graph(this.keys.length); Arrays.stream(text.split("\n")).forEach(line -> { String[] split = line.split(","); int v = this.map.get(split[0]); for (int i = 1; i < split.length; i++) { this.graph.addEdge(v, this.map.get(split[i])); } }); } public boolean contains(String key) { return map.contains(key); } public int index(String key) { return map.get(key); } public String name(int index) { return this.keys[index]; } public Graph graph() { return this.graph; } public static void main(String[] args) { System.out.println(Arrays.toString("323\n2323".split("\n"))); } }
文中全部源码已放入到了github仓库:
https://github.com/silently9527/JavaCore
文中或许会存在或多或少的不足、错误之处,有建议或者意见也很是欢迎你们在评论交流。
最后,写做不易,请不要白嫖我哟,但愿朋友们能够点赞评论关注三连,由于这些就是我分享的所有动力来源🙏