【恋上数据结构与算法 第二季】【04】图-基础实现_遍历_拓扑排序
持续学习&持续更新中…
学习态度:脚踏实地
【恋上数据结构与算法 第二季】【04】图-基础实现_遍历_拓扑排序
- 图的实现方案
- 邻接矩阵
- 邻接表
- 图的基础接口
- 顶点、边的定义
- 图的基础实现
- 图的遍历
- 广度优先搜索
- 深度优先搜索
- 修改遍历接口
- AOV网
- 拓扑排序
- 参考
代码实现图时并没有采用传统的邻接矩阵或者邻接表,因为它们都太复杂、太麻烦了。
代码实现采用了一种比较折中的方案,比较偏向于邻接表。
代码实现图时,一般实现为有向图,因为无向图可以用有向图来表达。
图的实现方案
邻接矩阵
邻接表
邻接表只需要一个一维数组
图的基础接口
public interface Graph<V, E> {int vertexSize(); // 顶点的数量int edgeSize(); // 边的数量void addVertex(V v); // 添加一个顶点void removeVertex(V v); // 删除一个顶点void addEdge(V from, V to); // 添加一条边void addEdge(V from, V to, E weight); // 添加一条边(带权值)void removeEdge(V from, V to); // 删除一条边
}
顶点、边的定义
public class ListGraph<V, E> implements Graph<V, E> {// 顶点private static class Vertex<V, E> {V value; // 顶点存储的元素Set<Edge<V, E>> inEdges = new HashSet<>(); // 以该顶点为终点的边(到达该顶点的边)Set<Edge<V, E>> outEdges = new HashSet<>(); // 以该顶点为起点的边(从该顶点出发的边)Vertex(V value) {this.value = value;}@Overridepublic boolean equals(Object o) {Vertex<V, E> vertex = (Vertex<V, E>) o;return Objects.equals(value, vertex.value);}@Overridepublic int hashCode() {return value != null ? value.hashCode() : 0;}@Overridepublic String toString() {return value == null ? "null" : value.toString();}}// 边private static class Edge<V, E> {E weight; // 边的权值Vertex<V, E> from; // 这条边从哪个顶点出发Vertex<V, E> to; // 这条边要到达哪个顶点Edge(Vertex<V, E> from, Vertex<V, E> to, E weight) {this.from = from;this.to = to;this.weight = weight;}@Overridepublic boolean equals(Object o) {Edge<V, E> edge = (Edge<V, E>) o;return from.equals(edge.from) && to.equals(edge.to);}@Overridepublic int hashCode() {int result = 0;result = 31 * result + from.hashCode();result = 31 * result + to.hashCode();return result;}@Overridepublic String toString() {return "Edge{" +"from=" + from +", to=" + to +", weight=" + weight +'}';}}
}
图的基础实现
public class ListGraph<V, E> implements Graph<V, E> {private final Map<V, Vertex<V, E>> vertices = new HashMap<>();private final Set<Edge<V, E>> edges = new HashSet<>();public void print() {System.out.println("顶点:");vertices.forEach((k, v) -> {System.out.println(k);System.out.println("in:");System.out.println(v.inEdges);System.out.println("out:");System.out.println(v.outEdges);System.out.println("---------------------");});System.out.println("边:");edges.forEach(System.out::println);}@Overridepublic int vertexSize() {return vertices.size();}@Overridepublic int edgeSize() {return edges.size();}@Overridepublic void addVertex(V v) {if (vertices.containsKey(v)) return;vertices.put(v, new Vertex<>(v));}@Overridepublic void removeVertex(V v) {// Vertex<V, E> vertex = vertices.get(v);
// if (null == vertex) return;final Vertex<V, E> removeVertex = vertices.remove(v);if (null == removeVertex) return;for (Iterator<Edge<V, E>> iterator = removeVertex.outEdges.iterator(); iterator.hasNext(); ) {final Edge<V, E> edge = iterator.next();edges.remove(edge);edge.to.inEdges.remove(edge);
// iterator.remove();}
// removeVertex.outEdges.clear();removeVertex.outEdges = null;for (Iterator<Edge<V, E>> iterator = removeVertex.inEdges.iterator(); iterator.hasNext(); ) {final Edge<V, E> edge = iterator.next();edges.remove(edge);edge.from.outEdges.remove(edge);
// iterator.remove();}
// removeVertex.inEdges.clear();removeVertex.inEdges = null;}@Overridepublic void addEdge(V from, V to) {addEdge(from, to, null);}// 如果发现某个顶点不存在那么需要创建该顶点@Overridepublic void addEdge(V from, V to, E weight) {Vertex<V, E> fromVertex = vertices.get(from);Vertex<V, E> toVertex = vertices.get(to);if (fromVertex == null) {fromVertex = new Vertex<>(from);vertices.put(from, fromVertex);}if (toVertex == null) {toVertex = new Vertex<>(to);vertices.put(to, toVertex);}Edge<V, E> edge = new Edge<>(fromVertex, toVertex, weight);if (fromVertex.outEdges.remove(edge)) { // 如果已经存在这条边了toVertex.inEdges.remove(edge);edges.remove(edge);}fromVertex.outEdges.add(edge);toVertex.inEdges.add(edge);edges.add(edge);}@Overridepublic void removeEdge(V from, V to) {Vertex<V, E> fromVertex = vertices.get(from);Vertex<V, E> toVertex = vertices.get(to);if (fromVertex == null || toVertex == null) {return;}Edge<V, E> edge = new Edge<>(fromVertex, toVertex, null);if (edges.remove(edge)) { // 如果存在这条边的话,才需要删除toVertex.inEdges.remove(edge);fromVertex.outEdges.remove(edge);}}// 顶点private static class Vertex<V, E> {V value; // 顶点存储的元素Set<Edge<V, E>> inEdges = new HashSet<>(); // 以该顶点为终点的边(到达该顶点的边)Set<Edge<V, E>> outEdges = new HashSet<>(); // 以该顶点为起点的边(从该顶点出发的边)Vertex(V value) {this.value = value;}@Overridepublic boolean equals(Object o) {Vertex<V, E> vertex = (Vertex<V, E>) o;return Objects.equals(value, vertex.value);}@Overridepublic int hashCode() {return value != null ? value.hashCode() : 0;}@Overridepublic String toString() {return value == null ? "null" : value.toString();}}// 边private static class Edge<V, E> {E weight; // 边的权值Vertex<V, E> from; // 这条边从哪个顶点出发Vertex<V, E> to; // 这条边要到达哪个顶点Edge(Vertex<V, E> from, Vertex<V, E> to, E weight) {this.from = from;this.to = to;this.weight = weight;}@Overridepublic boolean equals(Object o) {Edge<V, E> edge = (Edge<V, E>) o;return from.equals(edge.from) && to.equals(edge.to);}@Overridepublic int hashCode() {int result = 0;result = 31 * result + from.hashCode();result = 31 * result + to.hashCode();return result;}@Overridepublic String toString() {return "Edge{" +"from=" + from +", to=" + to +", weight=" + weight +'}';}}
}
图的遍历
广度优先搜索
使用广度优先搜索遍历图时,从不同的顶点出发,遍历到的结果是不一样的(需要注意的是,有的顶点有时是遍历不到的)
// 广度优先搜索@Overridepublic void bfs(V begin) {final Vertex<V, E> beginVertex = vertices.get(begin);if (null == beginVertex) return;Queue<Vertex<V, E>> queue = new LinkedList<>();Set<Vertex<V, E>> set = new HashSet<>();queue.offer(beginVertex);set.add(beginVertex);while (!queue.isEmpty()) {final Vertex<V, E> vertex = queue.poll();System.out.println(vertex); // 这里的遍历只是简单的打印一下顶点for (Edge<V, E> edge : vertex.outEdges) {if (set.contains(edge.to)) continue;queue.offer(edge.to);set.add(edge.to);}}}
深度优先搜索
使用深度优先搜索遍历图时,从不同的顶点出发,与广度优先搜索一样,遍历到的结果也会是不一样的(需要注意的是,有的顶点有时是遍历不到的);
使用深度优先搜索遍历图时,从相同的顶点出发进行遍历,也会有很多条路径可以走。
// 深度优先搜索——递归实现@Overridepublic void dfs(V begin) {final Vertex<V, E> beginVertex = vertices.get(begin);if (null == beginVertex) return;dfs(beginVertex, new HashSet<>());}private void dfs(Vertex<V, E> vertex, Set<Vertex<V, E>> set) {System.out.println(vertex);set.add(vertex);for (Edge<V, E> edge : vertex.outEdges) {if (set.contains(edge.to)) continue;dfs(edge.to, set);}}
// 深度优先搜索——非递归实现@Overridepublic void dfs(V begin) {final Vertex<V, E> beginVertex = vertices.get(begin);if (null == beginVertex) return;Set<Vertex<V, E>> set = new HashSet<>();Stack<Vertex<V, E>> stack = new Stack<>();stack.push(beginVertex);set.add(beginVertex);System.out.println(beginVertex);while (!stack.isEmpty()) {final Vertex<V, E> vertex = stack.pop();for (Edge<V, E> edge : vertex.outEdges) {if (set.contains(edge.to)) continue;stack.push(edge.from);stack.push(edge.to);set.add(edge.to);System.out.println(edge.to);break;}}}
修改遍历接口
public interface Graph<V, E> {int vertexSize(); // 顶点的数量int edgeSize(); // 边的数量void addVertex(V v); // 添加一个顶点void removeVertex(V v); // 删除一个顶点void addEdge(V from, V to); // 添加一条边 // 如果发现某个顶点不存在那么自动创建该顶点void addEdge(V from, V to, E weight); // 添加一条边(带权值) // 如果发现某个顶点不存在那么自动创建该顶点void removeEdge(V from, V to); // 删除一条边// void bfs(V begin); // 广度优先搜索遍历
// void dfs(V begin); // 深度优先搜索遍历void bfs(V begin, Visitor<V> visitor); // 广度优先搜索遍历void dfs(V begin, Visitor<V> visitor); // 深度优先搜索遍历interface Visitor<V> {void vertex(V v);}
}
// 广度优先搜索@Overridepublic void bfs(V begin, Visitor<V> visitor) {if (null == visitor) return;final Vertex<V, E> beginVertex = vertices.get(begin);if (null == beginVertex) return;Queue<Vertex<V, E>> queue = new LinkedList<>();Set<Vertex<V, E>> set = new HashSet<>();queue.offer(beginVertex);set.add(beginVertex);while (!queue.isEmpty()) {final Vertex<V, E> vertex = queue.poll();visitor.vertex(vertex.value);for (Edge<V, E> edge : vertex.outEdges) {if (set.contains(edge.to)) continue;queue.offer(edge.to);set.add(edge.to);}}}// 深度优先搜索——非递归实现@Overridepublic void dfs(V begin, Visitor<V> visitor) {if (null == visitor) return;final Vertex<V, E> beginVertex = vertices.get(begin);if (null == beginVertex) return;Set<Vertex<V, E>> set = new HashSet<>();Stack<Vertex<V, E>> stack = new Stack<>();stack.push(beginVertex);set.add(beginVertex);visitor.vertex(beginVertex.value);while (!stack.isEmpty()) {final Vertex<V, E> vertex = stack.pop();for (Edge<V, E> edge : vertex.outEdges) {if (set.contains(edge.to)) continue;stack.push(edge.from);stack.push(edge.to);set.add(edge.to);visitor.vertex(edge.to.value);break;}}// 深度优先搜索——递归实现public void dfs2(V begin, Visitor<V> visitor) {if (null == visitor) return;final Vertex<V, E> beginVertex = vertices.get(begin);if (null == beginVertex) return;dfs2(beginVertex, new HashSet<>(), visitor);}private void dfs2(Vertex<V, E> vertex, Set<Vertex<V, E>> set, Visitor<V> visitor) {visitor.vertex(vertex.value);set.add(vertex);for (Edge<V, E> edge : vertex.outEdges) {if (set.contains(edge.to)) continue;dfs2(edge.to, set, visitor);}}}
如果想要在遍历的过程中可以停止(退出)遍历的话:
void bfs(V begin, Visitor<V> visitor); // 广度优先搜索遍历void dfs(V begin, Visitor<V> visitor); // 深度优先搜索遍历
// 可以停止遍历interface Visitor<V> {boolean vertex(V v); // 返回true,就终止遍历}
// 可以停止遍历// 广度优先搜索@Overridepublic void bfs(V begin, Visitor<V> visitor) {if (null == visitor) return;final Vertex<V, E> beginVertex = vertices.get(begin);if (null == beginVertex) return;Queue<Vertex<V, E>> queue = new LinkedList<>();Set<Vertex<V, E>> set = new HashSet<>();queue.offer(beginVertex);set.add(beginVertex);while (!queue.isEmpty()) {final Vertex<V, E> vertex = queue.poll();if (visitor.vertex(vertex.value)) return;for (Edge<V, E> edge : vertex.outEdges) {if (set.contains(edge.to)) continue;queue.offer(edge.to);set.add(edge.to);}}}// 深度优先搜索——非递归实现@Overridepublic void dfs(V begin, Visitor<V> visitor) {if (null == visitor) return;final Vertex<V, E> beginVertex = vertices.get(begin);if (null == beginVertex) return;Set<Vertex<V, E>> set = new HashSet<>();Stack<Vertex<V, E>> stack = new Stack<>();stack.push(beginVertex);set.add(beginVertex);if (visitor.vertex(beginVertex.value)) return;while (!stack.isEmpty()) {final Vertex<V, E> vertex = stack.pop();for (Edge<V, E> edge : vertex.outEdges) {if (set.contains(edge.to)) continue;stack.push(edge.from);stack.push(edge.to);set.add(edge.to);if (visitor.vertex(edge.to.value)) return;break;}}}
AOV网
作业:自学《AOV网络》
拓扑排序
卡恩算法的思路是对的,但代码实现时,我们肯定不能将顶点删掉,因为这样会破坏原有的图结构,所以我们代码使用卡恩算法实现拓扑排序时,应该变通一下:
// 使用卡恩算法对DAG进行拓扑排序@Overridepublic List<V> topologicalSort() {List<V> list = new ArrayList<>(); // list用来存放遍历结果,返回给使用者Queue<Vertex<V, E>> queue = new LinkedList<>(); // queue用来存放入度为0的顶点Map<Vertex<V, E>, Integer> map = new HashMap<>(); // 顶点的入度表// 初始化入度表和queue(将度为0的节点都放入队列)vertices.forEach((v, vertex) -> {int inSize = vertex.inEdges.size();if (inSize == 0) queue.offer(vertex);else// 初始化入度表时,没有必要将入度为0的顶点放入入度表中map.put(vertex, inSize);});while (!queue.isEmpty()) {Vertex<V, E> vertex = queue.poll();list.add(vertex.value);vertex.outEdges.forEach(edge -> {Integer integer = map.get(edge.to);integer--;if (integer == 0)// 将这个顶点入队后,就不用更新该顶点的入度了queue.offer(edge.to);else map.put(edge.to, integer);});}return list;}
测试:
public class Data {public static final Object[][] TOPO = {{0, 2},{1, 0},{2, 5}, {2, 6},{3, 1}, {3, 5}, {3, 7},{5, 7},{6, 4},{7, 6}};
}
/*** 有向图*/private static Graph<Object, Double> directedGraph(Object[][] data) {Graph<Object, Double> graph = new ListGraph<>();for (Object[] edge : data) {if (edge.length == 1) {graph.addVertex(edge[0]);} else if (edge.length == 2) {graph.addEdge(edge[0], edge[1]);} else if (edge.length == 3) {double weight = Double.parseDouble(edge[2].toString());graph.addEdge(edge[0], edge[1], weight);}}return graph;}/*** 无向图*/private static Graph<Object, Double> undirectedGraph(Object[][] data) {Graph<Object, Double> graph = new ListGraph<>();for (Object[] edge : data) {if (edge.length == 1) {graph.addVertex(edge[0]);} else if (edge.length == 2) {graph.addEdge(edge[0], edge[1]);graph.addEdge(edge[1], edge[0]);} else if (edge.length == 3) {double weight = Double.parseDouble(edge[2].toString());graph.addEdge(edge[0], edge[1], weight);graph.addEdge(edge[1], edge[0], weight);}}return graph;}private static void testTopologicalSort() {Graph<Object, Double> graph = directedGraph(Data.TOPO);graph.topologicalSort().forEach(System.out::println);}public static void main(String[] args) {testTopologicalSort();}
参考
小码哥李明杰老师课程: 恋上数据结构与算法 第二季.
本文完,感谢您的关注支持!
【恋上数据结构与算法 第二季】【04】图-基础实现_遍历_拓扑排序相关推荐
- 《恋上数据结构与算法》第1季:算法概述
数据结构与算法的学习笔记目录:<恋上数据结构与算法>的学习笔记 目录索引 算法概述 1. 算法和数据结构 1.1 什么是算法 1.2 什么是数据结构 2. 时间复杂度 2.1 如何判断一个 ...
- 如何有效学习《恋上数据结构与算法》,更快地理解数据代码?
1.关于数据结构与算法? 数据结构就是为算法服务的,算法要作用在特定的数据结构之上.数据结构和算法相辅相成. 广义上讲就是 "操作一组数据的方法",像是你有很多个视频,我们怎么才能 ...
- MJ恋上数据结构(第1季 + 第2季)笔记
文章转载自:https://blog.csdn.net/weixin_43734095/article/details/104847976 恋上数据结构完整笔记(第1季 + 第2季) 前言 数据结构 ...
- 数据结构与算法A实验六图论---7-5 任务调度的合理性(拓扑排序)
假定一个工程项目由一组子任务构成,子任务之间有的可以并行执行,有的必须在完成了其它一些子任务后才能执行."任务调度"包括一组子任务.以及每个子任务可以执行所依赖的子任务集. 比如完 ...
- c++层次遍历_数据结构与算法,弄懂图的两种遍历方式
1 引言 遍历是指从某个节点出发,按照一定的的搜索路线,依次访问对数据结构中的全部节点,且每个节点仅访问一次. 在二叉树基础中,介绍了对于树的遍历.树的遍历是指从根节点出发,按照一定的访问规则, ...
- 插入排序算法 及其二分搜索优化版 C++代码实现 恋上数据结构笔记
复习梗概 文章目录 复习梗概 插入排序算法思想 插入排序时间复杂度与特性(多少,与什么有关?) 插入排序基础版 插入排序2nd优化版(优化了哪里?) !!!插入排序二分搜索优化版(优化了哪里?如何优化 ...
- 归并排序算法 C++实现与时间复杂度(考过)恋上数据结构笔记
复习梗概 画图,自己整个数组,看代码写步骤,这个对理解归并排序还是很有必要的 合并两个有序数组的merge函数写法 时间复杂度的分析方法!!! 其实我觉得去b站找个动态的步骤分解视频也是不错的复习方法 ...
- 快速排序 C++代码实现及其算法思想及时间复杂度分析及优化 恋上数据结构笔记
文章目录 复习梗概 算法思想 算法复杂度分析及稳定性 如何优化? 快速排序改进版代码C++ 快速排序个人青春版代码 完整代码 复习梗概 算法思想,别的排序名字直接就能让人联想到它的算法思想,唯独快速排 ...
- 《恋上数据结构第1季》二叉搜索树BST
二叉搜索树(BinarySearchTree) BST 接口设计 BST 基础 添加元素: add() 删除元素: remove() 删除节点 – 叶子节点 删除节点 – 度为1的节点 删除节点 – ...
最新文章
- python使用matplotlib可视化雷达图(polar函数可视化雷达图、极坐标图、通过径向方向来显示数据之间的关系)
- luogu P5292 [HNOI2019]校园旅行
- vrml场景实例代码_高并发的中断下半部tasklet实例解析
- tcp连接的三次握手
- mysql ---- innodb-2-索引
- LSGO软件技术团队2015~2016学年第五周(0928~1004)总结
- C语言过时了?你在做梦?
- macos可以升级到指定版本吗_承装承修承试可以跨级升级吗?
- python保存rtmp流_ffmpeg 推送、保存rtmp 流命令
- 兰州职称计算机中心,【兰州2013年职称计算机考试报名通知】- 环球网校
- Java基础篇之返回值
- Linux在文件中查找the字样,Linux文件查找
- Google 工作十年后,我选择离开!
- 下载百度翻译英文读音
- SAT写作例子之Frank Lloyd Wright
- 堆内存和栈内存的区别
- rmd中将html转为pdf,Rmd文件转化为PDF报告
- office2007每次打开都配置进度_win7下office2007总是配置进度怎么办-解决office2007显示配置进度的方法 - 河东软件园...
- cad角度命令怎么输入_CAD阵列命令中角度阵列的使用技巧
- 三年程序员生涯的感悟、总结和憧憬