力扣-拓扑排序
在图论中,**拓扑排序(Topological Sorting)**是一个有向无环图的所有顶点的线性序列,且该序列满足下面两个条件
1.每个顶点只出现一次
2.若在序列中顶点A出现在顶点B的前面 ,那么在途中不存在B到A的路径
TIPS:除了可以对有向无环图进行排序外,还可以看是不是排序的结果只有一种可能即是否存在分支,可以通过每次循环判断队列的长度来实现
-
329. 矩阵中的最长递增路径
我的题解:
思路: 有向无环图求最长路径可以考虑使用拓扑排序
1.我们初始化m,n,outdegrees,ans
2.我们遍历,matrix,并向四周进行扩展,若比当前元素大,就有一条边相连,outdegrees[i][j]++,初始化队列再次遍历outdegrees并将出度为0的元素加入队列中
3.遍历队列,若队列不为空,ans++,弹出队首元素,并向四周扩展,若该元素的四周有元素比当前元素小就度数-1,若度数为0就加入队列中
4.返回ansint[] fx = {0, 0, -1, 1}, fy = {-1, 1, 0, 0}; public int longestIncreasingPath(int[][] matrix) { //step1 int m = matrix.length, n = matrix[0].length, ans = 0; int[][] outdegrees = new int[m][n]; //step2 for (int i = 0; i < m; i++) { for (int j = 0; j < n; j++) { for (int k = 0; k < 4; k++) { int newX = i + fx[k], newY = j + fy[k]; if(inArea(newX, newY, m, n) && matrix[i][j] < matrix[newX][newY]){ outdegrees[i][j]++; } } } } Queue<int[]> queue = new ArrayDeque<>(); for (int i = 0; i < m; i++) { for (int j = 0; j < n; j++) { if(outdegrees[i][j] == 0) queue.offer(new int[]{i, j}); } } //step3 while(!queue.isEmpty()){ ans++; int size = queue.size(); for (int i = 0; i < size; i++) { int[] cur = queue.poll(); int x = cur[0], y = cur[1]; for (int k = 0; k < 4; k++) { int newX = x + fx[k], newY = y + fy[k]; if(inArea(newX, newY, m, n) && matrix[x][y] > matrix[newX][newY]){ outdegrees[newX][newY]--; if(outdegrees[newX][newY] == 0){ queue.offer(new int[] {newX, newY}); } } } } } return ans; } boolean inArea(int x, int y, int m, int n){ return x >= 0 && x < m && y >= 0 && y < n; }
-
210. 课程表 II
我的题解:
思路: 若a在b的前面则不存在,b -> a的路径,该问题可以用拓扑排序解决
1.我们初始化edges,indegrees和ans
2.我们遍历prerequisites将,对应的边加入edges中,并累加该点的入度,初始化队列,将入度为0的点加入队列中
3.当队列不为空,我们就弹出该点,并将该点加入ans中,遍历该点的边,并将指定点的,入度-1,判断是否有入度为0的点,若有就加入队列中
4.若index != numCourses则说明不可能完成所有的课程,返回空数组,否则返回anspublic int[] findOrder(int numCourses, int[][] prerequisites) { //step1 List<List<Integer>> edges = new ArrayList<>(); for (int i = 0; i < numCourses; i++) { edges.add(new ArrayList<>()); } int[] indegrees = new int[numCourses]; int[] ans = new int[numCourses]; //step2 for(int[] prerequisite : prerequisites){ int x = prerequisite[0], y = prerequisite[1]; edges.get(y).add(x); indegrees[x]++; } int idx = 0; Queue<Integer> queue = new ArrayDeque<>(); for (int i = 0; i < numCourses; i++) { if(indegrees[i] == 0) queue.offer(i); } //step3 while(!queue.isEmpty()){ int cur = queue.poll(); ans[idx++] = cur; for(int edge : edges.get(cur)){ indegrees[edge]--; if(indegrees[edge] == 0){ queue.offer(edge); } } } //step4 if(idx != numCourses) return new int[0]; return ans; }
-
剑指 Offer II 114. 外星文字典
我的题解:
思路: 本题要求我们对有向无环的图进行排序,可以用拓扑排序来解决,这是一道经典的拓扑排序题
步骤:
1.我们首先对map、indeg、n进行初始化操作,其中map是将题目中提供的words转化为图,indeg代表点的入度,用于判断排序规则是否合法
2.然后我们先对图和indeg进行初始化操作再根据题目提供的words进行构图操作并判断排序规则是否合法,若不合法直接返回空字符串
3.对map进行去重,初始化队列,初始化StringBuilder,并将入度为0的点加入队列中
4.若队列不为空,就弹出当前头结点,并将该字符加入进StringBuilder中,遍历与该点想连的点,并将相邻的点入度-1,若相邻点的入度为0就加入队列中
5.先判断StringBuilder的长度是否等于n,若相等则返回重构后的字符串,否则返回空字符串class Solution { //step1 Map<Character, List<Character>> map = new HashMap<>(); Map<Character, Integer> indeg = new HashMap<>(); int n = 0; public String alienOrder(String[] words) { int size = words.length; //step2 for (int i = 0; i < words.length; i++) { int sL = words[i].length(); char[] chars = words[i].toCharArray(); for (int j = 0; j < sL; j++) { if(!map.containsKey(chars[j])){ n++; map.put(chars[j], new ArrayList<>()); } if(!indeg.containsKey(chars[j])){ indeg.put(chars[j], 0); } } } for (int i = 1; i < size; i++) { if(!addEdge(words[i - 1], words[i])) return ""; } Queue<Character> queue = new ArrayDeque<>(); for(Map.Entry<Character, Integer> entry : indeg.entrySet()){ if(entry.getValue() == 0) queue.offer(entry.getKey()); } //step4 StringBuilder stringBuilder = new StringBuilder(n); while(!queue.isEmpty()){ char cha = queue.poll(); stringBuilder.append(cha); for(char c : map.get(cha)){ indeg.put(c, indeg.get(c) - 1); if(indeg.get(c) == 0) queue.offer(c); } } return stringBuilder.length() == n ? stringBuilder.toString() : ""; } /** * 添加边的方法,若判断边非法,则返回false * @param s1 * @param s2 * @return */ boolean addEdge(String s1, String s2){ int n1 = s1.length(), n2 = s2.length(); int len = Math.min(n1, n2); int idx = 0; while(idx < len){ char c1 = s1.charAt(idx), c2 = s2.charAt(idx); if(c1 != c2){ map.get(c1).add(c2); indeg.put(c2, indeg.get(c2) + 1); break; } idx++; } if(idx == n2 && n1 > n2) return false; return true; } }
-
剑指 Offer II 115. 重建序列
我的题解:
思路: 根据条件给定一个有向无环图的顺序,直接用拓扑排序,这里因为唯一的情况才能返回true,因此每执行一次循环我们就看queue中是不是只有一个元素
若不是则说明存在分支,因此直接返回false
步骤:
1.初始化map和indeg
2.根据条件将sequence中的有向边加入图中,并对点的入度进行累加
3.初始化队列和idx,遍历nums,看indeg中是否有该点,若没有则说明该点入度为0,直接加入队列中
4.若队列不为空,就获取当前队列的大小,若大小大于1则直接返回false,否则弹出当前元素,idx+1,并看当前该点的边,相连接的点入度-1,判断相连接的点入度是否为0,若为0加入队列中
5.若idx != nums的长度,则返回false,否则返回trueclass Solution { public boolean sequenceReconstruction(int[] nums, int[][] sequences) { //step1 Map<Integer, List<Integer>> map = new HashMap<>(); Map<Integer, Integer> indeg = new HashMap<>(); for(int i : nums){ map.putIfAbsent(i, new ArrayList<>()); } //step2 for(int[] arr : sequences){ for (int i = 1; i < arr.length; i++) { int prev = arr[i - 1], cur = arr[i]; map.get(prev).add(cur); indeg.put(cur, indeg.getOrDefault(cur, 0) + 1); } } //step3 int idx = 0; Queue<Integer> queue = new ArrayDeque<>(); for(int i : nums){ if(!indeg.containsKey(i)){ queue.offer(i); } } //step4 while(!queue.isEmpty()){ //判断是否存在多种可能,即存在分支 if(queue.size() > 1) return false; idx++; int i = queue.poll(); for(int edge : map.get(i)){ indeg.put(edge, indeg.get(edge) - 1); if(indeg.get(edge) == 0){ queue.offer(edge); } } } //step5 //判断是否有环 return idx == nums.length ? true : false; } }