力扣-拓扑排序

力扣-拓扑排序

在图论中,**拓扑排序(Topological Sorting)**是一个有向无环图的所有顶点的线性序列,且该序列满足下面两个条件

1.每个顶点只出现一次

2.若在序列中顶点A出现在顶点B的前面 ,那么在途中不存在B到A的路径

TIPS:除了可以对有向无环图进行排序外,还可以看是不是排序的结果只有一种可能即是否存在分支,可以通过每次循环判断队列的长度来实现

  1. 329. 矩阵中的最长递增路径

    我的题解:

    思路: 有向无环图求最长路径可以考虑使用拓扑排序
    1.我们初始化m,n,outdegrees,ans
    2.我们遍历,matrix,并向四周进行扩展,若比当前元素大,就有一条边相连,outdegrees[i][j]++,初始化队列再次遍历outdegrees并将出度为0的元素加入队列中
    3.遍历队列,若队列不为空,ans++,弹出队首元素,并向四周扩展,若该元素的四周有元素比当前元素小就度数-1,若度数为0就加入队列中
    4.返回ans

    int[] 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;
        }
    
  2. 210. 课程表 II
    我的题解:

    思路: 若a在b的前面则不存在,b -> a的路径,该问题可以用拓扑排序解决
    1.我们初始化edges,indegrees和ans
    2.我们遍历prerequisites将,对应的边加入edges中,并累加该点的入度,初始化队列,将入度为0的点加入队列中
    3.当队列不为空,我们就弹出该点,并将该点加入ans中,遍历该点的边,并将指定点的,入度-1,判断是否有入度为0的点,若有就加入队列中
    4.若index != numCourses则说明不可能完成所有的课程,返回空数组,否则返回ans

    public 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;
        }
    
  3. 剑指 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;
        }
    }
    
  4. 剑指 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,否则返回true

    class 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;
        }
    }
    
    
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值