算法原理
什么是拓扑排序?
1.有向无环图(DAG图)
例如在下图中:2可以到4,但4不能到2
有方向,能从一个点到另一个点,但不能反
入度:有多少条边指向他
出度:指向有多少条边

2.AOV网:定带你活动图
在有向无环图中,用顶点来表示一个活动,用边来表示活动的先后顺序的图结构
3.拓扑排序
找到做事情的先后顺序,拓扑排序的结果可能并不唯一(拓扑排序的重要应用:判断有向图是否有环)
如何进行拓扑排序:
1.找出图中入度为0的点,然后输出
2.删除有该点连接的边
3.重复1,2操作,直到图中没有点或者没有入度为0的点为止(有可能有环)
4.实现拓扑排序
借助队列,一次bfs即可
1.初始化:把所有入度为0的点加入到队列中
2.当队列不为空的时候: 1.拿出队头元素,加入到最终结果中 2.删除与该元素相连的边 3.删除:与删除边相邻的点,是否入度为0,如果入度为0加入到队列中
题目解析
1.课程表
https://leetcode.cn/problems/course-schedule/description/
题目描述
你这个学期比逊选修numCourses们课程,记为0-numCourses-1
在选修某些课程之前需要先修一些课程,先修课程按照prerequisites给出,其中prerequisite[i]=[ai,bi]表示如果要修ai课程必须先修bi课程
请你判断是否能够完成所有课程的学习,如果可以,返回true 如果不行,返回false
算法原理
这就是一个典型的拓扑排序,这里问能否完成所有课程的学习,其实本质上就是问有向图中是否有环
我们利用拓扑排序来解决这一问题
那么如何建图呢?(这要求我们灵活使用容器)
1.看数据量
领接矩阵/领接表
代码如何写? 1.List<List<Integer>> edges 2.Map<Integer,List<Integer>> edges (Map是比较万能的)
2.根据算法流程,灵活建图
如何找到入度 int[] in = new int[]
代码实现
java
class Solution {
public boolean canFinish(int n, int[][] p) {
Map<Integer,List<Integer>> map = new HashMap<>();
int[] in = new int[n];
for(int i = 0;i<p.length;i++){
int a = p[i][0];
int b = p[i][1];
if(!map.containsKey(b)){
map.put(b,new ArrayList<>());
}
map.get(b).add(a);
in[a]++;
}
Queue<Integer> q = new LinkedList<>();
for(int i = 0;i<n;i++){
if(in[i]==0){
q.add(i);
}
}
while(!q.isEmpty()){
int t = q.poll();
for(int a:map.getOrDefault(t,new ArrayList<>())){
in[a]--;
if(in[a]==0){
q.add(a);
}
}
}
for(int x :in){
if(x!=0){
return false;
}
}
return true;
}
}
2.课程表二
https://leetcode.cn/problems/course-schedule-ii/description/
题目描述
现在你共有numCourses们课程,记为0到numCourses-1 给你一个数组p,其中p[i]=[ai,bi] 表示在选修ai之前必须选修bi
返回你为了学完所有课程安排的学习顺序,返回任意一种即可 如果无法完成,则返回一个空数组
算法原理
这道题和上一道课程表类似,我们都是使用拓扑排序来解决,只不过不同的是这里我们需要返回一个学习顺序
我们只需要在出队列的时候,将这个课程加入返回的数组即可
最后如果j==n 我们直接返回我们得到的数组
如果j<n 说明未能学习完,返回一个空数组即可
代码实现
java
class Solution {
public int[] findOrder(int n, int[][] p) {
//1.准备工作
List<List<Integer>> edges=new ArrayList<>();
for(int i=0;i<n;i++){
edges.add(new ArrayList<>());
}
int[] in=new int[n];
//2.建图
for(int i=0;i<p.length;i++){
int a=p[i][0],b=p[i][1];
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.add(i);
}
}
int[] ret=new int[n];
int j=0;
while(!q.isEmpty()){
int t=q.poll();
ret[j++]=t;
for(int a:edges.get(t)){
in[a]--;
if(in[a]==0){
q.add(a);
}
}
}
if(j==n){
return ret;
}else{
return new int[]{};
}
}
}
java
class Solution {
public int[] findOrder(int n, int[][] p) {
Map<Integer,List<Integer>> map = new HashMap<>();
int[] in = new int[n];
for(int i=0;i<p.length;i++){
int a = p[i][0];
int b = p[i][1];
if(!map.containsKey(b)){
map.put(b,new ArrayList<>());
}
map.get(b).add(a);
in[a]++;
}
Queue<Integer> q = new LinkedList<>();
for(int i=0;i<n; i++){
if(in[i]==0){
q.add(i);
}
}
int[] ret = new int[n];
int j =0;
while(!q.isEmpty()){
int t = q.poll();
ret[j++] = t;
for(int a :map.getOrDefault(t,new ArrayList<>())){
in[a]--;
if(in[a]==0){
q.add(a);
}
}
}
if(j==n){
return ret;
}else{
return new int[]{};
}
}
}
3.火星词典
https://leetcode.cn/problems/Jf1JuT/description/
题目描述
现有一种使用英语字母的外星文语言,这门语言的字母顺序与英语顺序不用,给定一个字符串列表words,words中的字符串已经按照新语言的字母顺序进行了排序,请你按照该词典还原出此语言已知的字母顺序,并且按照字母递增顺序排列,如果不存在合法的字母顺序,返回"",如果存在多种,返回其中的一种即可
字符串s字典顺序小于字符串t有两种情况:
1.在第一个不同字母处,如果s中的字母在这门外星语言的字母顺序中位于t字母之前,那么s的字典顺序小于t
2.如果前面的(s.length,t.length)字母都相同,那么s.length<t.length时,s的字典顺序也小于t
算法原理
1.如何搜集信息
两次for循环,用双指针
找一个入度为0的,进行拓扑排序
2.拓扑排序
1.建图(用哈希表hash<char,hash<char>> edges)
2.统计入度信息 hash<char,int>in 必须要初始化
3,如何搜集信息
双指针(只找到第一个不同的位置) 这里注意细节问题:abc ab 这种是不合法的
当我们遇到abc ab 这样的时候,直接返回空
代码实现
java
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.add(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);
}
}
}
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){
//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;
}
}
}