你这个学期必须选修 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 inDegree = new HashMap<>();
// 将所有的课程先放入
for (int i = 0; i < numCourses; i++) {
inDegree.put(i, 0);
}
// 2.依赖关系, 依赖当前课程的后序课程
Map> 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 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 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, 返回fasle
for (int key : inDegree.keySet()) {
if (inDegree.get(key) != 0) {
return false;
}
}
return true;
}
}
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 = False
self.next=[None]*26
class Trie:
def __init__(self):
self.root=Node()
def insert(self, word: str) -> None:
cur=self.root
for 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 = True
def search(self, word: str) -> bool:
cur = self.root
for c in word:
c_i=ord(c)-ord('a')
if cur.next[c_i]:
cur=cur.next[c_i]
else:
return False
return cur.is_End
def startsWith(self, prefix: str) -> bool:
cur = self.root
for c in prefix:
c_i=ord(c)-ord('a')
if cur.next[c_i]:
cur=cur.next[c_i]
else:
return False
return 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)