mirror of
https://github.com/SunnyQjm/algorithm-review.git
synced 2026-06-03 08:16:43 +08:00
add: chapter11
This commit is contained in:
@@ -0,0 +1,90 @@
|
||||
#!/usr/bin/env python
|
||||
# coding=utf-8
|
||||
|
||||
#######################################################################################
|
||||
# Leetcode 208 实现 Trie (前缀树)
|
||||
#
|
||||
# 实现一个 Trie (前缀树),包含 insert, search, 和 startsWith 这三个操作。
|
||||
#
|
||||
# 示例:
|
||||
# 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
|
||||
#
|
||||
# 说明:
|
||||
# - 你可以假设所有的输入都是由小写字母 a-z 构成的。
|
||||
# - 保证所有输入均为非空字符串。
|
||||
#######################################################################################
|
||||
|
||||
class TreeNode:
|
||||
def __init__(self):
|
||||
self.word = False # 表示当前节点是否是一个曾经插入的单词
|
||||
self.children = {} # 子节点列表
|
||||
|
||||
class Trie:
|
||||
|
||||
def __init__(self):
|
||||
"""
|
||||
Initialize your data structure here.
|
||||
"""
|
||||
self.root = TreeNode()
|
||||
|
||||
|
||||
def insert(self, word: str) -> None:
|
||||
"""
|
||||
Inserts a word into the trie.
|
||||
"""
|
||||
# 从根节点出发
|
||||
node = self.root
|
||||
for char in word:
|
||||
if char not in node.children: # 判断当前字符是否在当前节点的子节点里面,不在,则创建一个子节点
|
||||
node.children[char] = TreeNode()
|
||||
node = node.children[char] # 跳到对应的子节点
|
||||
node.word = True
|
||||
|
||||
|
||||
def search(self, word: str) -> bool:
|
||||
"""
|
||||
Returns if the word is in the trie.
|
||||
"""
|
||||
node = self.root
|
||||
for char in word:
|
||||
if char not in node.children:
|
||||
return False
|
||||
node = node.children[char]
|
||||
return node.word
|
||||
|
||||
|
||||
def startsWith(self, prefix: str) -> bool:
|
||||
"""
|
||||
Returns if there is any word in the trie that starts with the given prefix.
|
||||
"""
|
||||
node = self.root
|
||||
for char in prefix:
|
||||
if char not in node.children:
|
||||
return False
|
||||
node = node.children[char]
|
||||
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)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
obj = Trie()
|
||||
obj.insert("apple")
|
||||
print(obj.search("apple"), "= True")
|
||||
print(obj.search("app"), "= False")
|
||||
print(obj.startsWith("app"), "= True")
|
||||
obj.insert("app")
|
||||
print(obj.search("app"), "= True")
|
||||
@@ -0,0 +1,59 @@
|
||||
#!/usr/bin/env python
|
||||
# coding=utf-8
|
||||
|
||||
#######################################################################################
|
||||
# Leetcode 720 词典中最长的单词
|
||||
#
|
||||
# 给出一个字符串数组words组成的一本英语词典。从中找出最长的一个单词,该单词是由words词典中其他单词逐步添加一个字母组成。若其中有多个可行的答案,则返回答案中字典序最小的单词。
|
||||
# 若无答案,则返回空字符串。
|
||||
#
|
||||
# 示例 1:
|
||||
# 输入:
|
||||
# words = ["w","wo","wor","worl", "world"]
|
||||
# 输出: "world"
|
||||
# 解释:
|
||||
# 单词"world"可由"w", "wo", "wor", 和 "worl"添加一个字母组成。
|
||||
#
|
||||
# 示例 2:
|
||||
# 输入:
|
||||
# words = ["a", "banana", "app", "appl", "ap", "apply", "apple"]
|
||||
# 输出: "apple"
|
||||
# 解释:
|
||||
# "apply"和"apple"都能由词典中的单词组成。但是"apple"得字典序小于"apply"。
|
||||
#
|
||||
# 注意:
|
||||
# - 所有输入的字符串都只包含小写字母。
|
||||
# - words数组长度范围为[1,1000]。
|
||||
# - words[i]的长度范围为[1,30]。
|
||||
#######################################################################################
|
||||
|
||||
from typing import List
|
||||
|
||||
class Solution:
|
||||
def longestWord(self, words: List[str]) -> str:
|
||||
"""
|
||||
:type words: List[str]
|
||||
:rtype str
|
||||
|
||||
(knowledge)
|
||||
|
||||
思路:
|
||||
1. 用一个集合valid存储满足题目中限定的若干字符串;
|
||||
2. 根据长度对原字符串列表排序,然后对排序后的字符串列表进行遍历;
|
||||
3. 对每个字符串word,判断word[:-1]是否在valid当中,在则将其也放入到valid当中
|
||||
4. 最后对valid进行按字典序排序,然后返回长度最大的元素即可
|
||||
"""
|
||||
|
||||
valid = set([""])
|
||||
|
||||
for word in sorted(words, key=len):
|
||||
if word[:-1] in valid:
|
||||
valid.add(word)
|
||||
|
||||
return max(sorted(valid), key=len)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
solution = Solution()
|
||||
words = ["a", "banana", "app", "appl", "ap", "apply", "apple"]
|
||||
print(solution.longestWord(words), "= apple")
|
||||
@@ -0,0 +1,76 @@
|
||||
#!/usr/bin/env python
|
||||
# coding=utf-8
|
||||
|
||||
#######################################################################################
|
||||
# Leetcode 105 从前序与中序遍历序列构造二叉树
|
||||
#
|
||||
# 根据一棵树的前序遍历与中序遍历构造二叉树。
|
||||
#
|
||||
# 注意:
|
||||
# 你可以假设树中没有重复的元素。
|
||||
#
|
||||
# 例如,给出
|
||||
# 前序遍历 preorder = [3,9,20,15,7]
|
||||
# 中序遍历 inorder = [9,3,15,20,7]
|
||||
#
|
||||
# 返回如下的二叉树:
|
||||
# 3
|
||||
# / \
|
||||
# 9 20
|
||||
# / \
|
||||
# 15 7
|
||||
#######################################################################################
|
||||
|
||||
from typing import List
|
||||
|
||||
class TreeNode:
|
||||
def __init__(self, x):
|
||||
self.val = x
|
||||
self.left = None
|
||||
self.right = None
|
||||
|
||||
def __repr__(self):
|
||||
if self:
|
||||
return "{}->{}->{}".format(self.val, repr(self.left), repr(self.right))
|
||||
|
||||
|
||||
class Solution:
|
||||
def buildTree(self, preorder: List[int], inorder: List[int]) -> TreeNode:
|
||||
"""
|
||||
:type preorder: List[int]
|
||||
:type inorder: List[int]
|
||||
:rtype TreeNode
|
||||
|
||||
(knowledge)
|
||||
|
||||
思路:
|
||||
1. 首先根据中序定位根节点;
|
||||
3 9 20 15 7
|
||||
_
|
||||
|
||||
2. 然后根据先序,划分左右子树;
|
||||
9 3 15 20 7
|
||||
_
|
||||
|
||||
3. 然后将左右子树部分各自递归的进行构建即可
|
||||
"""
|
||||
if not preorder:
|
||||
return None
|
||||
root = TreeNode(preorder[0])
|
||||
|
||||
# 用于记录左右子树的元素个数
|
||||
leftNum, rightNum = 0, 0
|
||||
|
||||
for num in inorder:
|
||||
if num == preorder[0]:
|
||||
break
|
||||
leftNum += 1
|
||||
|
||||
root.left = self.buildTree(preorder[1:1+leftNum], inorder[0:leftNum])
|
||||
root.right = self.buildTree(preorder[1+leftNum:], inorder[leftNum + 1:])
|
||||
return root
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
solution = Solution()
|
||||
solution.buildTree([3, 9, 20, 15, 7], [9, 3, 15, 20, 7])
|
||||
@@ -0,0 +1,92 @@
|
||||
#!/usr/bin/env python
|
||||
# coding=utf-8
|
||||
|
||||
#######################################################################################
|
||||
# Leetcode 997 找到小镇的法官
|
||||
#
|
||||
# 在一个小镇里,按从 1 到 N 标记了 N 个人。传言称,这些人中有一个是小镇上的秘密法官。
|
||||
# 如果小镇的法官真的存在,那么:
|
||||
# 1. 小镇的法官不相信任何人。
|
||||
# 2. 每个人(除了小镇法官外)都信任小镇的法官。
|
||||
# 3. 只有一个人同时满足属性 1 和属性 2 。
|
||||
# 给定数组 trust,该数组由信任对 trust[i] = [a, b] 组成,表示标记为 a 的人信任标记为 b 的人。
|
||||
# 如果小镇存在秘密法官并且可以确定他的身份,请返回该法官的标记。否则,返回 -1。
|
||||
#
|
||||
# 示例 1:
|
||||
# 输入:N = 2, trust = [[1,2]]
|
||||
# 输出:2
|
||||
#
|
||||
# 示例 2:
|
||||
# 输入:N = 3, trust = [[1,3],[2,3]]
|
||||
# 输出:3
|
||||
#
|
||||
# 示例 3:
|
||||
# 输入:N = 3, trust = [[1,3],[2,3],[3,1]]
|
||||
# 输出:-1
|
||||
#
|
||||
# 示例 4:
|
||||
# 输入:N = 3, trust = [[1,2],[2,3]]
|
||||
# 输出:-1
|
||||
#
|
||||
# 示例 5:
|
||||
# 输入:N = 4, trust = [[1,3],[1,4],[2,3],[2,4],[4,3]]
|
||||
# 输出:3
|
||||
#
|
||||
# 提示:
|
||||
# 1. 1 <= N <= 1000
|
||||
# 2. trust.length <= 10000
|
||||
# 3. trust[i] 是完全不同的
|
||||
# 4. trust[i][0] != trust[i][1]
|
||||
# 5. 1 <= trust[i][0], trust[i][1] <= N
|
||||
#######################################################################################
|
||||
|
||||
from typing import List
|
||||
|
||||
|
||||
class Solution:
|
||||
def findJudge(self, N: int, trust: List[List[int]]) -> int:
|
||||
"""
|
||||
:type N: int
|
||||
:type trust: List[List[int]]
|
||||
:rtype int
|
||||
|
||||
(knowledge)
|
||||
|
||||
思路:
|
||||
1. 将整个信任关系构建成一个图,a信任b表示a到b之间有条a->b的有向边;
|
||||
2. 很容易直到,符合条件的法官要求入度为N-1,出度为0;
|
||||
3. 所以只要找到入度为N-1的点,并判断其出度是否为0即可。
|
||||
"""
|
||||
# 特判只有一个人的情况,不构成图
|
||||
if N == 1:
|
||||
return 1
|
||||
|
||||
# 用两个字典分别记录入度和出度
|
||||
inDegree, outDegree = {}, {}
|
||||
|
||||
# 遍历所有的边,记录所有的出度入度
|
||||
for trust_element in trust:
|
||||
if trust_element[0] in outDegree:
|
||||
outDegree[trust_element[0]] += 1
|
||||
else:
|
||||
outDegree[trust_element[0]] = 1
|
||||
if trust_element[1] in inDegree:
|
||||
inDegree[trust_element[1]] += 1
|
||||
else:
|
||||
inDegree[trust_element[1]] = 1
|
||||
|
||||
# 遍历入度记录表,找到入度为N-1的人,并判断其出度是否为0
|
||||
for key, value in inDegree.items():
|
||||
if value == N - 1 and key not in outDegree:
|
||||
return key
|
||||
|
||||
return -1
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
solution = Solution()
|
||||
print(solution.findJudge(2, [[1, 2]]), "= 2")
|
||||
print(solution.findJudge(3, [[1, 3], [2, 3]]), "= 3")
|
||||
print(solution.findJudge(3, [[1, 3], [2, 3], [3, 1]]), "= -1")
|
||||
print(solution.findJudge(3, [[1, 2], [2, 3]]), "= -1")
|
||||
print(solution.findJudge(4, [[1, 3], [1, 4], [2, 3], [2, 4], [4, 3]]), "= 3")
|
||||
@@ -0,0 +1,94 @@
|
||||
#!/usr/bin/env python
|
||||
# coding=utf-8
|
||||
|
||||
#######################################################################################
|
||||
# Leetcode 102 二叉树的层序遍历
|
||||
#
|
||||
# 给你一个二叉树,请你返回其按 层序遍历 得到的节点值。 (即逐层地,从左到右访问所有节点)。
|
||||
#
|
||||
# 示例:
|
||||
# 二叉树:[3,9,20,null,null,15,7],
|
||||
#
|
||||
# 3
|
||||
# / \
|
||||
# 9 20
|
||||
# / \
|
||||
# 15 7
|
||||
# 返回其层次遍历结果:
|
||||
#
|
||||
# [
|
||||
# [3],
|
||||
# [9,20],
|
||||
# [15,7]
|
||||
# ]
|
||||
#######################################################################################
|
||||
|
||||
from typing import List
|
||||
import collections
|
||||
|
||||
class TreeNode:
|
||||
def __init__(self, x):
|
||||
self.val = x
|
||||
self.left = None
|
||||
self.right = None
|
||||
|
||||
def __repr__(self):
|
||||
if self:
|
||||
return "{}->{}->{}".format(self.val, repr(self.left), repr(self.right))
|
||||
|
||||
class Solution:
|
||||
def levelOrder(self, root: TreeNode) -> List[List[int]]:
|
||||
"""
|
||||
:type root: TreeNode
|
||||
:rtype List[List[int]]
|
||||
|
||||
(knowledge)
|
||||
|
||||
思路:
|
||||
1. 采用图的BFS遍历的思想对树进行逐层遍历;
|
||||
2. 用queue来实现BFS;(由于这边是树型结构,所以不会有环路,故不需要用visited来记录哪些节点已经访问过)
|
||||
3. 为了记录当前遍历的节点在哪一层,我们用两个变量curLevelNum和nextLevelNum来分别记录当前层次还有多少没有遍历,以及下一层次还有多少没有遍历
|
||||
|
||||
tip: ppt中用的是DFS的方法,那种方法可以更容易的记录当前节点所属层级
|
||||
"""
|
||||
|
||||
# 特判树为空的情况
|
||||
if not root:
|
||||
return []
|
||||
|
||||
# 初始时curLevelNum = 1表示要遍历的第一层为根节点只有一个
|
||||
# 初始时nextLevelNum = 0,当遍历节点时,会增加下一层的可遍历节点数量
|
||||
curLevelNum, nextLevelNum = 1, 0
|
||||
result = [[]]
|
||||
queue = collections.deque([root])
|
||||
|
||||
while queue:
|
||||
curNode = queue.popleft()
|
||||
result[-1].append(curNode.val)
|
||||
curLevelNum -= 1
|
||||
|
||||
# 判断当前节点的左右节点是否存在,存在则入队
|
||||
if curNode.left:
|
||||
queue.append(curNode.left)
|
||||
nextLevelNum += 1
|
||||
if curNode.right:
|
||||
queue.append(curNode.right)
|
||||
nextLevelNum += 1
|
||||
|
||||
if curLevelNum == 0: # 判断是否换层
|
||||
curLevelNum, nextLevelNum = nextLevelNum, 0
|
||||
# 如果下一层还有可遍历的节点,则往结果集里面新加一个空列表用来存储下一层的遍历结果
|
||||
if curLevelNum != 0:
|
||||
result.append([])
|
||||
|
||||
return result
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
solution = Solution()
|
||||
root = TreeNode(3)
|
||||
root.left = TreeNode(9)
|
||||
root.right = TreeNode(20)
|
||||
root.right.left = TreeNode(15)
|
||||
root.right.right = TreeNode(7)
|
||||
print(solution.levelOrder(root), "= [[3], [9, 20], [15, 7]]")
|
||||
@@ -0,0 +1,57 @@
|
||||
#!/usr/bin/env python
|
||||
# coding=utf-8
|
||||
|
||||
#######################################################################################
|
||||
# Leetcode 104 二叉树的最大深度
|
||||
#
|
||||
# 给定一个二叉树,找出其最大深度。
|
||||
# 二叉树的深度为根节点到最远叶子节点的最长路径上的节点数。
|
||||
# 说明: 叶子节点是指没有子节点的节点。
|
||||
#
|
||||
# 示例:
|
||||
# 给定二叉树 [3,9,20,null,null,15,7],
|
||||
#
|
||||
# 3
|
||||
# / \
|
||||
# 9 20
|
||||
# / \
|
||||
# 15 7
|
||||
# 返回它的最大深度 3 。
|
||||
#######################################################################################
|
||||
|
||||
class TreeNode:
|
||||
def __init__(self, x):
|
||||
self.val = x
|
||||
self.left = None
|
||||
self.right = None
|
||||
|
||||
def __repr__(self):
|
||||
if self:
|
||||
return "{}->{}->{}".format(self.val, repr(self.left), repr(self.right))
|
||||
|
||||
|
||||
class Solution:
|
||||
def maxDepth(self, root: TreeNode) -> int:
|
||||
"""
|
||||
:type root: TreeNode
|
||||
:rtype int
|
||||
|
||||
(knowledge)
|
||||
|
||||
思路:
|
||||
1. 使用先序方法遍历,在遍历过程中传递当前层级深度;
|
||||
2. 使用递归方式实现
|
||||
"""
|
||||
if not root:
|
||||
return 0
|
||||
return max(self.maxDepth(root.left), self.maxDepth(root.right)) + 1
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
solution = Solution()
|
||||
root = TreeNode(3)
|
||||
root.left = TreeNode(9)
|
||||
root.right = TreeNode(20)
|
||||
root.right.left = TreeNode(15)
|
||||
root.right.right = TreeNode(7)
|
||||
print(solution.maxDepth(root), "= 3")
|
||||
@@ -0,0 +1,59 @@
|
||||
#!/usr/bin/env python
|
||||
# coding=utf-8
|
||||
|
||||
#######################################################################################
|
||||
# Leetcode 111 二叉树的最小深度
|
||||
#
|
||||
# 给定一个二叉树,找出其最小深度。
|
||||
# 最小深度是从根节点到最近叶子节点的最短路径上的节点数量。
|
||||
# 说明: 叶子节点是指没有子节点的节点。
|
||||
#
|
||||
# 示例:
|
||||
# 给定二叉树 [3,9,20,null,null,15,7],
|
||||
#
|
||||
# 3
|
||||
# / \
|
||||
# 9 20
|
||||
# / \
|
||||
# 15 7
|
||||
# 返回它的最小深度 2.
|
||||
#######################################################################################
|
||||
|
||||
class TreeNode:
|
||||
def __init__(self, x):
|
||||
self.val = x
|
||||
self.left = None
|
||||
self.right = None
|
||||
|
||||
def __repr__(self):
|
||||
if self:
|
||||
return "{}->{}->{}".format(self.val, repr(self.left), repr(self.right))
|
||||
|
||||
|
||||
class Solution:
|
||||
def minDepth(self, root: TreeNode) -> int:
|
||||
"""
|
||||
:type root: TreeNode
|
||||
:rtype int
|
||||
|
||||
(knowledge)
|
||||
|
||||
思路:
|
||||
1. 通过DFS递归遍历目标树,一旦遍历到叶子节点,马上返回
|
||||
"""
|
||||
if root is None:
|
||||
return 0
|
||||
|
||||
if root.left and root.right:
|
||||
return min(self.minDepth(root.left), self.minDepth(root.right)) + 1
|
||||
return max(self.minDepth(root.left), self.minDepth(root.right)) + 1
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
solution = Solution()
|
||||
root = TreeNode(3)
|
||||
root.left = TreeNode(9)
|
||||
root.right = TreeNode(20)
|
||||
root.right.left = TreeNode(15)
|
||||
root.right.right = TreeNode(7)
|
||||
print(solution.minDepth(root), "= 2")
|
||||
Reference in New Issue
Block a user