3.207. 课程表
你这个学期必须选修 numCourses
门课程,记为 0
到 numCourses - 1
。
在选修某些课程之前需要一些先修课程。 先修课程按数组 prerequisites
给出,其中 prerequisites[i] = [ai, bi]
,表示如果要学习课程 ai
则 必须 先学习课程 bi
。
- 例如,先修课程对
[0, 1]
表示:想要学习课程0
,你需要先完成课程1
。
请你判断是否可能完成所有课程的学习?如果可以,返回 true
;否则,返回 false
。
示例 1:
输入:numCourses = 2, prerequisites = [[1,0]] 输出:true 解释:总共有 2 门课程。学习课程 1 之前,你需要完成课程 0 。这是可能的。示例 2:
输入:numCourses = 2, prerequisites = [[1,0],[0,1]] 输出:false 解释:总共有 2 门课程。学习课程 1 之前,你需要先完成课程 0 ;并且学习课程 0 之前,你还应先完成课程 1 。这是不可能的。提示:
1 <= numCourses <= 2000
0 <= prerequisites.length <= 5000
prerequisites[i].length == 2
0 <= ai, bi < numCourses
prerequisites[i]
中的所有课程对 互不相同
思路:
1.首先构建一个邻接表,并开始遍历prerequisites数组,记录每一个顶点(一门课)的入度。prerequisites[i]
=(m,n)表示学习m前必须学习n,则m的入度+1,邻接表key=m对应value数组添加n。
2.再次遍历,每次选择入度为0的顶点,入队,并将判断将相关顶点入度-1。
3.最后判断所有入度是否为0,是则可以修完,否则不可以。
代码:
class Solution {// 节点的入度: 使用数组保存每个节点的入度,public boolean canFinish(int numCourses, int[][] prerequisites) {// 1.课号和对应的入度Map<Integer, Integer> inDegree = new HashMap<>();// 将所有的课程先放入for (int i = 0; i < numCourses; i++) {inDegree.put(i, 0);}// 2.依赖关系, 依赖当前课程的后序课程Map<Integer, List<Integer>> adj = new HashMap<>();// 初始化入度和依赖关系for (int[] relate : prerequisites) {// (3,0), 想学3号课程要先完成0号课程, 更新3号课程的入度和0号课程的依赖(邻接表)int cur = relate[1];int next = relate[0];// 1.更新入度inDegree.put(next, inDegree.get(next) + 1);// 2.当前节点的邻接表if (!adj.containsKey(cur)) {adj.put(cur, new ArrayList<>());}adj.get(cur).add(next);}// 3.BFS, 将入度为0的课程放入队列, 队列中的课程就是没有先修, 可以学的课程Queue<Integer> q = new LinkedList<>();for (int key : inDegree.keySet()) {if (inDegree.get(key) == 0) {q.offer(key);}}// 取出一个节点, 对应学习这门课程.// 遍历当前邻接表, 更新其入度; 更新之后查看入度, 如果为0, 加入到队列while (!q.isEmpty()) {int cur = q.poll();// 遍历当前课程的邻接表, 更新后继节点的入度if (!adj.containsKey(cur)) {continue;}List<Integer> successorList = adj.get(cur);for (int k : successorList) {inDegree.put(k, inDegree.get(k) - 1);if (inDegree.get(k) == 0) {q.offer(k);}}}// 4.遍历入队, 如果还有课程的入度不为0, 返回faslefor (int key : inDegree.keySet()) {if (inDegree.get(key) != 0) {return false;}}return true;}
}
4.208. 实现 Trie (前缀树)
Trie(发音类似 "try")或者说 前缀树 是一种树形数据结构,用于高效地存储和检索字符串数据集中的键。这一数据结构有相当多的应用情景,例如自动补完和拼写检查。
请你实现 Trie 类:
Trie()
初始化前缀树对象。void insert(String word)
向前缀树中插入字符串word
。boolean search(String word)
如果字符串word
在前缀树中,返回true
(即,在检索之前已经插入);否则,返回false
。boolean startsWith(String prefix)
如果之前已经插入的字符串word
的前缀之一为prefix
,返回true
;否则,返回false
。
示例:
输入 ["Trie", "insert", "search", "search", "startsWith", "insert", "search"] [[], ["apple"], ["apple"], ["app"], ["app"], ["app"], ["app"]] 输出 [null, null, true, false, true, null, true]解释 Trie trie = new Trie(); trie.insert("apple"); trie.search("apple"); // 返回 True trie.search("app"); // 返回 False trie.startsWith("app"); // 返回 True trie.insert("app"); trie.search("app"); // 返回 True提示:
1 <= word.length, prefix.length <= 2000
word
和prefix
仅由小写英文字母组成insert
、search
和startsWith
调用次数 总计 不超过3 * 10^4
次
思路:
这一篇非常好
力扣(LeetCode)官网 - 全球极客挚爱的技术成长平台
代码:
python版:
class Node:def __init__(self):self.is_End = Falseself.next=[None]*26class Trie:def __init__(self):self.root=Node()def insert(self, word: str) -> None:cur=self.rootfor c in word:c_i=ord(c)-ord('a')if not cur.next[c_i]:cur.next[c_i]=Node()cur = cur.next[c_i]cur.is_End = Truedef search(self, word: str) -> bool:cur = self.rootfor c in word:c_i=ord(c)-ord('a')if cur.next[c_i]:cur=cur.next[c_i]else:return Falsereturn cur.is_Enddef startsWith(self, prefix: str) -> bool:cur = self.rootfor c in prefix:c_i=ord(c)-ord('a')if cur.next[c_i]:cur=cur.next[c_i]else:return Falsereturn True# Your Trie object will be instantiated and called as such:
# obj = Trie()
# obj.insert(word)
# param_2 = obj.search(word)
# param_3 = obj.startsWith(prefix)