Bootstrap

【优选算法】(第四十六篇)

目录

课程表II(medium)

题目解析

讲解算法原理

编写代码

⽕星词典(hard)

题目解析

讲解算法原理

编写代码


课程表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;
 }
}

;