【BFS专题】— 解决拓扑排序问题

拓扑排序介绍:

1、课程表 - 力扣(LeetCode)

思路:

  1. 通过Map<Integer, List<Integer>> 来创建邻接图,数组来表示入度
  2. 然后遍历课程数组,建图
  3. 然后再拓扑排序,bfs
  4. 最后在遍历入度数组,判断有没有环,即是不是入度都为零
  5. 代码:
    class Solution {
        public boolean canFinish(int n, int[][] p) {
            //1、准备工作
            //统计每个顶点的如度
            int[] in = new int[n];
            //构造邻接图表
            Map<Integer, List<Integer>> edges = new HashMap<>();
            //2、建图
            for(int i = 0; i < p.length; i++){
                int a = p[i][0];
                int b = p[i][1];
                if(!edges.containsKey(b)){
                    edges.put(b, new ArrayList<>());
                }
                edges.get(b).add(a);
                //入度加一
                in[a]++;
            }
    
            //3、拓扑排序
            //先把入度为零的点加入到对列中
            Queue<Integer> q = new LinkedList<>();
            for(int i = 0; i < n; i++){
                if(in[i] == 0){
                    q.offer(i);
                }
            }
            //bfs
            while(!q.isEmpty()){
                int t = q.poll();
                for(int a : edges.getOrDefault(t, new ArrayList<>())){
                    in[a]--;
                    if(in[a] == 0){
                        q.offer(a);
                    }
                }
            }
            //4、判断是否有环
            for(int x : in){
                if(x != 0){
                    return false;
                }
            }
            return true;
        }
    }

2、课程表 II - 力扣(LeetCode) 

思路:

  1. 和上一题一样,把最后出队的元素加入到数组中即可
  2. 代码:
    public int[] findOrder(int n, int[][] p) {
            //准备工作
            List<List<Integer>> edges = new ArrayList<>();
            int[] in = new int[n];
            for(int i = 0; i < n; i++){
                edges.add(new ArrayList<>());
            }
            //建图
            for(int i = 0; i < p.length; i++){
                int a = p[i][0];
                int b = p[i][1];
                edges.get(b).add(a);
                in[a]++;
            }
            //拓扑排序
            //入度为0 的点入队
            Queue<Integer> q = new LinkedList<>();
            for(int i = 0; i < n; i++){
                if(in[i] == 0){
                    q.add(i);
                }
            }
            //bfs
            int[] ret = new int[n];
            int index = 0;
            while(!q.isEmpty()){
                int t = q.poll();
                ret[index++] = t;
                for(int a : edges.get(t)){
                    in[a]--;
                    if(in[a] == 0){
                        q.add(a);
                    }
                }
            }
            //判断
            if(index == n){
                return ret;
            }else{
                return new int[0];
            }
        }

3、 火星词典 - 力扣(LeetCode)

思路:

  1. 拓扑排序
    建图:用Map<Character, Set<Character>>
    统计入度:Map<Character, Integer>
  2. 细节问题:当第一组的字符串长度大于第二组的字符串时,不合法
  3. 代码:
    class Solution {
        Map<Character, Set<Character>> edges = new HashMap<>();//邻接表
        Map<Character, Integer> in = new HashMap<>();//统计每个节点的入度
        boolean check;
        public String alienOrder(String[] words) {
            //1、初始化入度的哈希表和建邻接表
            for(String s : words){
                for(int i = 0; i < s.length(); i++){
                    char ch = s.charAt(i);
                    in.put(ch, 0);
                }
            }
            int n = words.length;
            for(int i = 0; i < n; i++){
                for(int j = i + 1; j < n; j++){
                    add(words[i],words[j]);
                    if(check == true){
                        return "";
                    }
                }
            }
            //2、拓扑排序
            Queue<Character> q = new LinkedList<>();
            //将入度为零的节点都入队
            for(char ch : in.keySet()){
                if(in.get(ch) == 0){
                    q.offer(ch);
                }
            }
            StringBuffer ret = new StringBuffer();
            //遍历队列
            while(!q.isEmpty()){
                char t = q.poll();
                ret.append(t);
                if(!edges.containsKey(t)){
                    continue;
                }
                for(char ch : edges.get(t)){
                    in.put(ch, in.get(ch) - 1);
                    if(in.get(ch) == 0){
                        q.add(ch);
                    }
                }
            }
            //3、判断,是否入度都是为零
            for(char ch : in.keySet()){
                if(in.get(ch) != 0){
                    return "";
                }
            }
            return ret.toString();
        }
        //处理字符串
        public void add(String s1, String s2){
            int n = Math.min(s1.length(), s2.length());
            int i = 0;
            for( ; i < n; i++){
                char c1 = s1.charAt(i);
                char c2 = s2.charAt(i);
                if(c1 != c2){
                    if(!edges.containsKey(c1)){
                        edges.put(c1, new HashSet<>());
                    }
                    if(!edges.get(c1).contains(c2)){
                        edges.get(c1).add(c2);
                        in.put(c2, in.get(c2)+1);
                    }
                    break;
                }
            }
            if(i == s2.length() && i < s1.length()){
                check = true;
            }
        }
    }

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值