图,拓扑排序

210. 课程表 II

class Solution {
    public int[] findOrder(int numCourses, int[][] prerequisites) {
        // 邻接表建图
        ArrayList<ArrayList<Integer>> graph = new ArrayList<>();
        // 注意课程数是0~numCourses-1
        // 初始化
        for(int i=0;i<numCourses;i++){
            graph.add(new ArrayList<>());
        }
        // 入度表
        int[] indegree = new int[numCourses];
        // 在遍历数组的同时,建图,并完成入度表
        for(int[] edge : prerequisites){
            // from的邻居就是to
            graph.get(edge[1]).add(edge[0]);
            indegree[edge[0]]++;
        }
        // 用数组实现的队列收集入度为0的点
        int[] queue = new int[numCourses];
        // 初始化队列
        int l=0;
        int r=0;
        for(int i=0;i<numCourses;i++){
            if(indegree[i]==0){
                // 加入队列,r++
                queue[r++]=i;
            }
        }
        // 统计队列弹出的节点总数
        int count=0;
        // 依次弹出队列中的节点,直至队列为空
        while(l<r){
            // 弹出节点,l++
            int cur = queue[l++];
            count++;
            // 消除节点影响,即将当前节点的邻居的入度--
            for(int next: graph.get(cur)){
                if(--indegree[next]==0){
                    queue[r++]=next;
                }
            }
        }
        // 当弹出的节点数与课程数相等时,就返回队列,否则返回一个空数组
        return count==numCourses ? queue :new int[0];
    }
}

936. 戳印序列

class Solution {
    public int[] movesToStamp(String stamp, String target) {
        char[] s=stamp.toCharArray();
        char[] t = target.toCharArray();
        int m=s.length;
        int n=t.length;
        // 创建一个入度表,统计每个索引开头的序列错误数,长度为n-m+1;
        int[] indegree = new int[n-m+1];
        // 初始认为所有点的入度都为m,即每个索引开头的序列全错
        Arrays.fill(indegree,m);
        // 建图,初始化
        ArrayList<ArrayList<Integer>> graph=new ArrayList<>();
        for(int i=0; i<n;i++){
            graph.add(new ArrayList<>());
        }
        // 创建队列,加入入度为0的点
        int[] queue =new int[n-m+1];
        int l=0,r=0;
        // 遍历目标字符串,以每个索引开头的序列与戳印进行比较  
        for(int i=0; i<=n-m;i++){
            // 从i+0, i+1,... ,i+m-1
            for(int j=0; j<m;j++){
                // 比对成功一个位置就--
                if(t[i+j]==s[j]){
                    if(--indegree[i]==0){
                        queue[r++]=i;
                    }
                }else{
                    // 建图,from:错误的位置,to:影响的开头索引i
                    graph.get(i+j).add(i);
                }
            }
        }
        // 用一个数组标记该位置是否被取消错误,不要重复统计
        boolean[] visited = new boolean[n];
        // 记录结果的数组
        int[] path=new int[n-m+1];
        // 统计次数,也就是需要戳的位置个数
        int size=0;
        while(l<r){
            int cur=queue[l++];
            path[size++]=cur;
            for(int i=0;i<m;i++){
                // 当弹出的位置的错误未被取消时,进行如下操作
                // 得到该位置影响的开头索引,将入度--,即消除当前错误的影响
                if(!visited[cur+i]){
                    visited[cur+i]=true;
                    for(int next : graph.get(cur+i)){
                        if(--indegree[next]==0){
                            queue[r++]=next;
                        }
                    }
                }
            }
        }
        // 当size个数达不到要求,返回一个空数组
        if(size!=n-m+1){
            return new int[0];
        }
        // 这里需要将path逆序
        // 因为数组中入度为0的在前面,即不存在错误的情况,这个位置肯定是最后一次戳
        for(int i=0, j=size-1;i<j;i++,j--){
            int temp=path[i];
            path[i]=path[j];
            path[j]=temp;
        }
        return path;
    }
}

851. 喧闹和富有

class Solution {
    public int[] loudAndRich(int[][] richer, int[] quiet) {
        int n =quiet.length;
        // 建图
        ArrayList<ArrayList<Integer>> graph = new ArrayList<>();
        for(int i=0;i<n;i++){
            graph.add(new ArrayList<>());
        }
        // 创建入度表,统计每个点的入度
        int[] indegree = new int[n];
        for(int[] r : richer){
            graph.get(r[0]).add(r[1]);
            indegree[r[1]]++;
        }
        // 创建数组实现的队列
        int[] queue = new int[n];
        int l=0, r=0;
        // 先将入度为0的点加入队列
        for(int i=0; i<n; i++){
            if(indegree[i]==0){
                queue[r++]=i;
            }
        }
        // 存储结果的数组
        int[] ans = new int[n];
        // 一开始认为最安静的人就是自己
        for(int i=0; i<n ; i++){
            ans[i]=i;
        }
        while(l<r){
            int cur = queue[l++];
            for(int next : graph.get(cur)){
                // 这个过程中涉及到将上游节点的消息推送给下游节点
                // 即当前节点的安静值与下一个节点的安静值比较过程中,
                // 当前节点的安静值是他上游节点比他富有的人且安静值最小的
                if(quiet[ans[cur]]< quiet[ans[next]]){
                    ans[next]=ans[cur];
                }
                if(--indegree[next]==0){
                    queue[r++]=next;
                }
            }
        }
        return ans;
    }
}

2050. 并行课程 III

class Solution {
    public int minimumTime(int n, int[][] relations, int[] time) {
        // 邻接表建图
        ArrayList<ArrayList<Integer>> graph = new ArrayList<>();
        for(int i=0;i<=n;i++){
            graph.add(new ArrayList<>());
        }
        // 入度表
        int[] indegree = new int[n+1];
        for(int[] edge : relations){
            graph.get(edge[0]).add(edge[1]);
            indegree[edge[1]]++;
        }
        // 队列
        int[] queue =new int[n];
        // 初始化,将入度为0的点加入队列
        int l=0,r=0;
        for(int i=1; i<=n; i++){
            if(indegree[i]==0){
                queue[r++]=i;
            }
        }
        // 用一个数组来记录每个节点需花费的最少时间
        int[] cost = new int[n+1];
        // 记录最终的结果
        int ans=0;
        while (l < r) {
            int cur =  queue[l++];
            //  time[i] 表示完成第 (i+1) 门课程需要花费的 月份 数。
            // 每个点的花费时间为前面推来的和加上自己需要的时间
            cost[cur]+=time[cur-1];
            ans=Math.max(ans,cost[cur]);
            for(int next : graph.get(cur)){
                // 将邻居值更新为与当前值比较最大的
                cost[next]=Math.max(cost[next],cost[cur]);
                if(--indegree[next]==0){
                    queue[r++]=next;
                }
            }
        }
        return ans;
    }
}

2127. 参加会议的最多员工数

class Solution {
    public int maximumInvitations(int[] favorite) {
        // 该题不用建图,给的favorite就是图,因为每个人只有一个邻居
        // 统计入度
        int n = favorite.length;
        int[] indegree = new int[n];
        for(int i = 0 ; i<n;i++){
            indegree[favorite[i]]++;
        }
        // 队列
        int[] queue = new int[n];
        int l=0, r=0;
        for(int i=0;i<n;i++){
            if(indegree[i]==0){
                queue[r++]=i;
            }
        }
        // deep[i] : 不包括i在内,i之前的最长链的长度
        int[] deep=new int[n];
        while(l<r){
            int cur = queue[l++];
            int next =favorite[cur];
            deep[next]=Math.max(deep[next],deep[cur]+1);
            if(--indegree[next]==0){
                queue[r++]=next;
            }
        }
        // 在上一步中把入度为0以及消除影响后入度为0的点都删除了
        // 目前图中剩下的就是在环上的点
        // 可能性1:所有的小环(中心个数==2),
        // 总个数为中心点数+各中心点的最长链
        int sumOfSmallRings=0;
        // 可能性2:所有大环(中心个数>2)的中心点数
        int bigRings=0;
        for(int i = 0;i<n;i++){
            if(indegree[i]>0){
                int ringsize =1;
                indegree[i]=0;
                for(int j = favorite[i];j!=i;j=favorite[j]){
                    ringsize++;
                    indegree[j]=0;
                }
                if(ringsize==2){
                    sumOfSmallRings += 2+deep[i]+deep[favorite[i]];
                }else{
                    bigRings=Math.max(bigRings,ringsize);
                }
            }
        }
        return Math.max(sumOfSmallRings,bigRings);

    }
}

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值