算法练习(18)-图的拓扑排序
如上图,假设有一个大型代码工程,里面有5个模块:
模块1依赖模块2
模块2依赖模块3和模块5
模块3依赖模块4和模块5
那么,项目在编译时,应该按怎样的的顺序编译? 这就是所谓的拓扑排序问题
就这个示例而言,显然正确的编译顺序是:5->4->3->2->1 或 4->5->3->2->1 (注:4与5之间没有相互依赖,谁先谁后都可以)
思路:如下图,先找出入度为0的节点,然后以它为源点,依次把相邻节点的入度减1,然后再以下1个入度为0的点做为起点,依次反复,直到最后所有节点的入度都为0,最后把这个过程中经过的入度为0的点,倒过来,就是正确的顺序。
算法并不复杂,但问题在于,如果面试中遇到该题,通常给的输入并非图结构,可能是个二维数据,比如:
int[][] arr = new int[][]{ new int[]{1, 2}, new int[]{2, 5}, new int[]{2, 3}, new int[]{3, 4}, new int[]{3, 5} };
第1个数字表示自身,第2个数字表示下1个依赖的节点,这种情况,只要转换成上一篇里的Graph结构即可:
Graph convert(int[][] arr) { List<Node> nodes = new ArrayList<>(); List<Edge> edges = new ArrayList<>(); HashMap<Integer, Node> map = new HashMap<>(); for (int[] items : arr) { int curVal = items[0]; int nextVal = items[1]; Node cur = map.get(curVal); Node next = map.get(nextVal); if (cur == null) { cur = new Node(curVal); map.put(curVal, cur); } if (next == null) { next = new Node(nextVal); map.put(nextVal, next); } if (!cur.nexts.contains(next)) { cur.nexts.add(next); next.in++; } if (!nodes.contains(cur)) { nodes.add(cur); } if (!nodes.contains(next)) { nodes.add(next); } } Graph g = new Graph(nodes, edges); return g; }
注:本算法中,并不需要用到边Edge,所以上面的转换过程,并没有处理Edge.
接下来,就可以开始搞拓扑排序了:
import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Stack; public class GraphTest { Graph convert(int[][] arr) { List<Node> nodes = new ArrayList<>(); List<Edge> edges = new ArrayList<>(); HashMap<Integer, Node> map = new HashMap<>(); for (int[] items : arr) { int curVal = items[0]; int nextVal = items[1]; Node cur = map.get(curVal); Node next = map.get(nextVal); if (cur == null) { cur = new Node(curVal); map.put(curVal, cur); } if (next == null) { next = new Node(nextVal); map.put(nextVal, next); } if (!cur.nexts.contains(next)) { cur.nexts.add(next); next.in++; } if (!nodes.contains(cur)) { nodes.add(cur); } if (!nodes.contains(next)) { nodes.add(next); } } Graph g = new Graph(nodes, edges); return g; } /** * 拓扑排序 * * @param g 有向无环图 * @return */ List<Integer> topologicalSort(Graph g) { //找出入度为0的节点 Node first = null; for (Node node : g.nodes) { if (node.in == 0) { first = node; break; } } //相邻节点入度-1, 如减到0,则入栈 Stack<Node> stack = new Stack<>(); stack.add(first); while (first.nexts.size() != 0) { for (Node next : first.nexts) { next.in--; if (next.in == 0) { stack.add(next); first = next; } } } //弹出结果 List<Integer> list = new ArrayList<>(); while (!stack.isEmpty()) { list.add(stack.pop().value); } return list; } public static void main(String[] args) { GraphTest t = new GraphTest(); int[][] arr = new int[][]{ new int[]{1, 2}, new int[]{2, 5}, new int[]{2, 3}, new int[]{3, 4}, new int[]{3, 5} }; Graph g = t.convert(arr); List<Integer> result = t.topologicalSort(g); System.out.println(result); } }
输出:[5, 4, 3, 2, 1]
作者:菩提树下的杨过
出处:http://yjmyzz.cnblogs.com
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。
出处:http://yjmyzz.cnblogs.com
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。