目录
课程表II(medium)
题目解析
1.题目链接:. - 力扣(LeetCode)
2.题目描述
现在你总共有 numCourses ⻔课需要选,记为 0 到 numCourses - 1 。给你⼀个数组
prerequisites ,其中 prerequisites[i] = [a(i), b(i)] ,表⽰在选修课程a(i) 前必须先选修 b(i) 。
• 例如,想要学习课程 0 ,你需要先完成课程 1 ,我们⽤⼀个匹配来表⽰: [0,1] 。返回你为了学完所有课程所安排的学习顺序。可能会有多个正确的顺序,你只要返回任意⼀种就可
以了。如果不可能完成所有课程,返回⼀个空数组。
⽰例1:
输⼊:numCourses=2,prerequisites=[[1,0]]
输出:[0,1]
解释:总共有2⻔课程。要学习课程1,你需要先完成课程0。因此,正确的课程顺序为[0,1]。⽰例2:
输⼊:numCourses=4,prerequisites=[[1,0],[2,0],[3,1],[3,2]]
输出:[0,2,1,3]
解释:总共有4⻔课程。要学习课程3,你应该先完成课程1和课程2。并且课程1和课程2都应该排在课程0之后。[0,1,2,3]。另⼀个正确的排序是[0,2,1,3]。
⽰例3:
输⼊:numCourses=1,prerequisites=[]
输出:[0]
提⽰:
◦ 1 <= numCourses <= 2000
◦ 0 <= prerequisites.length <= numCourses * (numCourses - 1)
◦ prerequisites[i].length == 2
◦ 0 <= a(i), b(i) < numCourses
◦ a(i) != b(i)
◦ 所有 [a(i), b(i)] 互不相同
讲解算法原理
解法:
算法思路:
和上⼀题⼀样~
编写代码
c++算法代码:
class Solution
{
public:
vector<int> findOrder(int numCourses, vector<vector<int>>& prerequisites)
{
// 1. 准备⼯作
vector<vector<int>> edges(numCourses); // 邻接表存储图
vector<int> in(numCourses); // 存储每⼀个点的⼊度
// 2. 建图
for(auto& p : prerequisites)
{
int a = p[0], b = p[1]; // b -> a
edges[b].push_back(a);
in[a]++;
}
// 3. 拓扑排序
vector<int> ret; // 统计排序后的结果
queue<int> q;
for(int i = 0; i < numCourses; i++)
{
if(in[i] == 0) q.push(i);
}
while(q.size())
{
int t = q.front(); q.pop();
ret.push_back(t);
for(int a : edges[t])
{
in[a]--;
if(in[a] == 0) q.push(a);
}
}
if(ret.size() == numCourses) return ret;
else return {};
}
};
java算法代码:
class Solution
{
public int[] findOrder(int n, int[][] prerequisites)
{
// 1. 准备⼯作
int[] in = new int[n]; // 统计每个顶点的⼊度
List<List<Integer>> edges = new ArrayList<>();
for(int i = 0; i < n; i++)
{
edges.add(new ArrayList<>());
}
// 2. 建图
for(int i = 0; i < prerequisites.length; i++)
{
int a = prerequisites[i][0], b = prerequisites[i][1]; // b -> a
edges.get(b).add(a);
in[a]++;
}
// 3. 拓扑排序
Queue<Integer> q = new LinkedList<>();
int[] ret = new int[n];
int index = 0;
for(int i = 0; i < n; i++)
{
if(in[i] == 0) q.add(i);
}
while(!q.isEmpty())
{
int t = q.poll();
ret[index++] = t;
for(int a : edges.get(t))
{
in[a]--;
if(in[a] == 0) q.add(a);
}
}
if(index == n) return ret;
return new int[0];
}
}
⽕星词典(hard)
题目解析
1.题目链接:. - 力扣(LeetCode)
2.题目描述
现有⼀种使⽤英语字⺟的外星⽂语⾔,这⻔语⾔的字⺟顺序与英语顺序不同。
给定⼀个字符串列表 words ,作为这⻔语⾔的词典, words 中的字符串已经按这⻔新语⾔的字⺟顺序进⾏了排序。
请你根据该词典还原出此语⾔中已知的字⺟顺序,并按字⺟递增顺序排列。若不存在合法字⺟顺序,返回 "" 。若存在多种可能的合法字⺟顺序,返回其中任意⼀种顺序即可。
字符串 s 字典顺序⼩于字符串 t 有两种情况:
• 在第⼀个不同字⺟处,如果 s 中的字⺟在这⻔外星语⾔的字⺟顺序中位于 t 中字⺟之前,那
么 s 的字典顺序⼩于 t 。
• 如果前⾯ min(s.length, t.length) 字⺟都相同,那么 s.length < t.length
时, s 的字典顺序也⼩于 t 。
⽰例1:
输⼊:words=["wrt","wrf","er","ett","rftt"]
输出:"wertf"
⽰例2:
输⼊:words=["z","x"]
输出:"zx"
⽰例3:
输⼊:words=["z","x","z"]
输出:""
解释:不存在合法字⺟顺序,因此返回""。
提⽰:
◦ 1 <= words.length <= 100
◦ 1 <= words[i].length <= 100
◦ words[i] 仅由⼩写英⽂字⺟组成
讲解算法原理
解法:
算法思路:
将题意搞清楚之后,这道题就变成了判断有向图时候有环,可以⽤拓扑排序解决。
如何搜集信息(如何建图):
a. 两层for循环枚举出所有的两个字符串的组合;
b. 然后利⽤指针,根据字典序规则找出信息。
编写代码
c++算法代码:
class Solution
{
unordered_map<char, unordered_set<char>> edges; // 邻接表来存储图 unordered_map<char, int> in; // 统计⼊度
bool cheak; // 处理边界情况
public:
string alienOrder(vector<string>& words)
{
// 1. 建图 + 初始化⼊度哈希表
for(auto& s : words)
{
for(auto ch : s)
{
in[ch] = 0;
}
}
int n = words.size();
for(int i = 0; i < n; i++)
{
for(int j = i + 1; j < n; j++)
{
add(words[i], words[j]);
if(cheak) return "";
}
}
// 2. 拓扑排序
queue<char> q;
for(auto& [a, b] : in)
{
if(b == 0) q.push(a);
}
string ret;
while(q.size())
{
char t = q.front(); q.pop();
ret += t;
for(char ch : edges[t])
{
if(--in[ch] == 0) q.push(ch);
}
}
// 3. 判断
for(auto& [a, b] : in)
if(b != 0) return "";
return ret;
}
void add(string& s1, string& s2)
{
int n = min(s1.size(), s2.size());
int i = 0;
for( ; i < n; i++)
{
if(s1[i] != s2[i])
{
char a = s1[i], b = s2[i]; // a -> b
if(!edges.count(a) || !edges[a].count(b))
{
edges[a].insert(b);
in[b]++;
}
break;
}
}
if(i == s2.size() && i < s1.size()) cheak = true;
}
};
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);
}
}
// 3. 判断
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), 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;
}
}