diff --git a/chapter11/1_implement-trie.py b/chapter11/1_implement-trie.py new file mode 100644 index 0000000..e0ceb80 --- /dev/null +++ b/chapter11/1_implement-trie.py @@ -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") diff --git a/chapter11/2_longest-word-in-dictionary.py b/chapter11/2_longest-word-in-dictionary.py new file mode 100644 index 0000000..a90f369 --- /dev/null +++ b/chapter11/2_longest-word-in-dictionary.py @@ -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") diff --git a/chapter11/3_construct-binary-tree-from-preorder-and-inorder-traversal.py b/chapter11/3_construct-binary-tree-from-preorder-and-inorder-traversal.py new file mode 100644 index 0000000..5f729cd --- /dev/null +++ b/chapter11/3_construct-binary-tree-from-preorder-and-inorder-traversal.py @@ -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]) diff --git a/chapter11/4_find-the-town-judge.py b/chapter11/4_find-the-town-judge.py new file mode 100644 index 0000000..a1bf321 --- /dev/null +++ b/chapter11/4_find-the-town-judge.py @@ -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") diff --git a/chapter11/5_binary-tree-level-order-traversal.py b/chapter11/5_binary-tree-level-order-traversal.py new file mode 100644 index 0000000..9d46060 --- /dev/null +++ b/chapter11/5_binary-tree-level-order-traversal.py @@ -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]]") diff --git a/chapter11/6_maximum-depth-of-binary-tree.py b/chapter11/6_maximum-depth-of-binary-tree.py new file mode 100644 index 0000000..38fed89 --- /dev/null +++ b/chapter11/6_maximum-depth-of-binary-tree.py @@ -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") diff --git a/chapter11/7_minimum-depth-of-binary-tree.py b/chapter11/7_minimum-depth-of-binary-tree.py new file mode 100644 index 0000000..c5eec44 --- /dev/null +++ b/chapter11/7_minimum-depth-of-binary-tree.py @@ -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")