文章目录
- 拓扑排序
- [1. 课程表(LC207)](#1. 课程表(LC207))
- [2. 课程表II(LC210)](#2. 课程表II(LC210))
- [3. 火星词典(LCR114)](#3. 火星词典(LCR114))
拓扑排序
借助队列,一次BFS即可
- 初始化,把所有AOV网中入度为0的点加入到队列中
- 当队列不为空时:
- 拿出队头元素放到结果中
- 删除与该节点相连的边
- 判断:与删除边相连的节点是否入度变成0,如果入度为0,加入到队列中
建图:
- 用
List<List<Integer>或者Map<Integer,List<Integer>>实现领接表 - 利用数组
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)
题目描述

解题思路
在出队时统计拓扑排序
代码实现
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)
题目描述

解题思路
-
收集信息:两层for循环,统计所有的字母组合。
-
拓扑排序:
- 建图:利用
Map<Charater , Set<Character>>存放边的信息。不直接用List,而是要换成Set,防止重复的信息干扰 - 统计入度信息:
Map<Character,Integer>,数组可以自动初始化为0,而哈希表必须手动初始化所有的节点入度为0
- 建图:利用
-
判断字典序:一个指针分别遍历两个字符串,当指针遇到不同字符时,前面的字符比后面的字典序小。如果遇到类似
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();
}
}