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);
}
}