【广度优先搜索】BFS解决拓扑排序:课程表I,课程表II,火星词典

文章目录

拓扑排序

借助队列,一次BFS即可

  1. 初始化,把所有AOV网中入度为0的点加入到队列中
  2. 当队列不为空时:
    1. 拿出队头元素放到结果中
    2. 删除与该节点相连的边
    3. 判断:与删除边相连的节点是否入度变成0,如果入度为0,加入到队列中

建图:

  1. List<List<Integer>或者Map<Integer,List<Integer>>实现领接表
  2. 利用数组int[] in = new int[n]存储每个节点的入度

1. 课程表(LC207)

课程表

题目描述

代码实现

java 复制代码
class Solution {
    public boolean canFinish(int numCourses, int[][] prerequisites) {
        Queue<Integer> queue = new LinkedList<>();
        //统计入度
        int[] in = new int[numCourses];
        //邻接表存图
        Map<Integer,List<Integer>> edge = new HashMap<>();

        //建图
        for(int i =0;i<prerequisites.length;i++){
            //b->a
            int a = prerequisites[i][0];
            int b = prerequisites[i][1];
            if(!edge.containsKey(b))
                edge.put(b,new ArrayList<>());
            edge.get(b).add(a);
            in[a]++;
        }

        //拓扑排序
        //1.把入度为0 的点加入到队列中
        for(int i = 0;i<numCourses;i++){
            if(in[i]==0)
                queue.offer(i);
        }
        //bfs
        while(!queue.isEmpty()){
            int t = queue.poll();
            //遍历当前点所连接的下一个节点,让它们入度-1
            for(int a : edge.getOrDefault(t,new ArrayList<>())){
                in[a]--;
                if(in[a]==0)
                    queue.offer(a);
            }
        }

        //判断是否有环
        for(int a :in){
            if(a!=0)
                return false;
        }
        return true;
    }
}

2. 课程表II(LC210)

课程表II

题目描述

解题思路

在出队时统计拓扑排序

代码实现

java 复制代码
class Solution {
    public int[] findOrder(int numCourses, int[][] prerequisites) {
        int index = 0;
        int[] ret = new int[numCourses];
        Queue<Integer> queue = new LinkedList<>();
        Map<Integer,List<Integer>> map = new HashMap<>();
        int[] in = new int[numCourses];

        //建图
        for(int i = 0;i<prerequisites.length;i++){
            int a = prerequisites[i][0];
            int b = prerequisites[i][1];
            if(!map.containsKey(b))
                map.put(b,new ArrayList());
            map.get(b).add(a);
            in[a]++;
        }

        //拓扑排序
        for (int i = 0; i < numCourses; i++) { 
            if (in[i] == 0) {
                queue.offer(i);
            }
        }

        //bfs
        while(!queue.isEmpty()){
            int t = queue.poll();
            ret[index++]=t;
            for(int a : map.getOrDefault(t,new ArrayList<>())){
                in[a]--;
                if(in[a]==0)
                    queue.offer(a);
            }
        }

        if(index==numCourses)
            return ret;
        return new int[0];
    }
}

3. 火星词典(LCR114)

火星词典

题目描述

解题思路

  1. 收集信息:两层for循环,统计所有的字母组合。

  2. 拓扑排序:

    1. 建图:利用Map<Charater , Set<Character>> 存放边的信息。不直接用List,而是要换成Set,防止重复的信息干扰
    2. 统计入度信息:Map<Character,Integer>,数组可以自动初始化为0,而哈希表必须手动初始化所有的节点入度为0
  3. 判断字典序:一个指针分别遍历两个字符串,当指针遇到不同字符时,前面的字符比后面的字典序小。如果遇到类似abc,ab这种前长后短的字符串,可以直接判定为无效。

代码实现

java 复制代码
class Solution {
    public String alienOrder(String[] words) {
        HashMap<Character,HashSet<Character>> edges = new HashMap<>();
        HashMap<Character,Integer> in = new HashMap<>();
        StringBuilder ret = new StringBuilder();

        //初始化哈希表入度,把所有字符的入度都置为0
        for(String s:words){
            for(int i = 0;i<s.length();i++)
                in.put(s.charAt(i),0);
        }

        //收集信息
        for(int i = 0;i<words.length;i++){
            for(int j = i+1;j<words.length;j++){
                String a = words[i];
                String b = words[j];

                int index = 0;
                while(index<a.length() && index<b.length() && a.charAt(index) == b.charAt(index))
                    index++;

                //a包含b且a比b长,非法
                if(index<a.length() && index==b.length())
                    return "";
                
                //字母不同,判断字典序
                if(index<a.length() && index<b.length()){
                    char ch1 = a.charAt(index);
                    char ch2 = b.charAt(index);
                    //ch1 < ch2

                    if(!edges.containsKey(ch1))
                        edges.put(ch1,new HashSet<Character>()); 
                    
                    if(!edges.get(ch1).contains(ch2)){
                        edges.get(ch1).add(ch2);
                        in.put(ch2,in.get(ch2)+1);
                    }
                }
            }
        }
        //  拓扑排序
        Queue<Character> queue = new LinkedList<>();
        //把所有入度为0的节点放入队列中
        for(char ch :in.keySet()){
            if(in.get(ch)==0)
                queue.offer(ch);
        }

        while(!queue.isEmpty()){
            char top = queue.poll();
            ret.append(top);

            for(char ch:edges.getOrDefault(top,new HashSet<>())){
                in.put(ch,in.get(ch)-1);
                if(in.get(ch)==0)
                    queue.offer(ch);
            }
        }
        for(char ch :in.keySet()){
            if(in.get(ch)!=0)
                return "";
        }
        return ret.toString();
    }
}
相关推荐
用户938515635071 小时前
从 O(n²) 到 O(nlogn):一文读懂快速排序的“快”与“妙”
javascript·算法
To_OC2 小时前
手写快排次次翻车?别死背快排模板了,这才是面试官想听的底层逻辑
javascript·算法·排序算法
饼干哥哥3 小时前
Reddit VOC调研太慢?搭一个AI专家团队半小时洞察任何品类|以猫用饮水机为例
人工智能·算法·ai编程
地平线开发者4 小时前
Transformer模型部署之性能优化指南
算法
地平线开发者4 小时前
人在途中:从“编译失败”到“模型可落地”——CUDA 自定义算子
算法·自动驾驶
半个落月7 小时前
从递归到快速排序:用 JavaScript 把分治思想讲明白
javascript·算法·面试
小月土星8 小时前
JavaScript 快速排序:从 pivot、双指针到分治思想
javascript·算法·面试
小月土星8 小时前
JavaScript 递归入门:从 1 到 n 求和,再到数组扁平化
javascript·算法·面试
To_OC1 天前
LC 1 两数之和:面试第一道必考题,暴力解法直接被面试官 pass
javascript·算法·leetcode