938. Range Sum of BST
https://leetcode.com/problems/range-sum-of-bst/
Given the
root
node of a binary search tree, return the sum of values of all nodes with value betweenL
andR
(inclusive).The binary search tree is guaranteed to have unique values.
因为是二叉搜索树,所以不必遍历每一个节点。比如说,一个节点的值小于等于$L$,那么它的左子树上的节点都小于$L$,因为不必再遍历它的左子树。按照这个思路,可以在DFS时剪枝。时间复杂度平均情况下为$O(N)$,空间复杂度为$O(H)$,其中$N$为节点数,$H$为树高度。
def rangeSumBST(self, root: TreeNode, L: int, R: int) -> int:
if not root:
return 0
ans = 0
if L <= root.val <= R:
ans += root.val
if root.val > L:
ans += self.rangeSumBST(root.left, L, R)
if root.val < R:
ans += self.rangeSumBST(root.right, L, R)
return ans
894. All Possible Full Binary Trees
https://leetcode.com/problems/all-possible-full-binary-trees
A full binary tree is a binary tree where each node has exactly 0 or 2 children.
Return a list of all possible full binary trees with
N
nodes. Each element of the answer is the root node of one possible tree.Each
node
of each tree in the answer must havenode.val = 0
.You may return the final list of trees in any order.
一个FBT一定有单数个节点。所以如果给的数字$N$是偶数,那么直接返回空列表。对于一个单数$N$,首先分配$1$给根节点,然后依次给左子树分配$2M$,给右子树分配$N-1-2M$,其中$1\leq M\leq (N-1)/2$。
class Solution:
def allPossibleFBT(self, N: int):
# in each FBT, there must be an odd number of nodes
if N % 2 == 0:
return []
if N == 1:
# only a root
return [TreeNode(0)]
res = [] # result
# a root + 2*n internal nodes/leaves
for left in range(1, N, 2):
# we can build the left sub FBT with odd number of nodes
left_subtree = self.allPossibleFBT(left)
right_subtree = self.allPossibleFBT(N-1-left)
# Cartesian product of left_subtree and right_subtree
for ls in left_subtree:
for rs in right_subtree:
root = TreeNode(0)
root.left = ls
root.right = rs
res.append(root)
return res
时间复杂度设为$f(N)$,可以通过观察循环体,发现:
因此时间复杂度的下界为$\Omega(2^N)$。空间复杂度与FST数目有关,也可以用类似的方法求出。
761*. Special Binary String
https://leetcode.com/problems/special-binary-string/
Special binary strings are binary strings with the following two properties:
The number of 0’s is equal to the number of 1’s.
Every prefix of the binary string has at least as many 1’s as 0’s.
Given a special string
S
, a move consists of choosing two consecutive, non-empty, special substrings ofS
, and swapping them. (Two strings are consecutive if the last character of the first string is exactly one index before the first character of the second string.)At the end of any number of moves, what is the lexicographically largest resulting string possible?
这道题我不会做,是看这个答案的。我在看这篇答案之后,想了一会才明白是怎么回事。我认为这道题最关键的一点,就是要清楚:通过代码中计算count的方法找出的S[i:j]
如果是一个special string,那么S[i+1:j-1]
的任何一个子串,都不会和S[:i+1]
,S[j:]
的子串进行交换,即,special string内部的子串不会“跑出”这个special string。这是因为:
- special string
S[i:j]
的内部子串S[i+1:j-1]
,仍然是一个special string。这是由于special string两个条件的限制。S[i:j]
的首尾两个字符肯定是1和0,因为它的前缀总是要保持1的数目不小于0。同时,由于S[i:k]
,i < k < j
,中,1的数目总是大于0,所以S[i+1:k]
中1的数目不小于0,满足第二个条件。且S[i+1:j-1]
中1的数目等于0,可以推出它是一个special string。 S[k:v]
,i < k < j < v
,不可能是一个special string。假设它是一个special string,那么S[k:j]
中1的数目不小于0。由于S[j-1]
是0,所以S[k:j-1]
中1的数目大于0。同时,S[i+1:j-1]
中1的数目等于0的数目,因此S[i:k]
中0的数目大于1。这与第二个条件矛盾。因此,S[k:v]
不是一个special string,它就不会跟后面的special string交换,S[k:j]
就保留在S[i:j]
内部。S[w:k]
,w < i < k < j
也是同理。
因此,就可以通过计算count的方法,将S
划分成若干special string。这些special string可以两两换位,因此它们之间的排序是随意的。只需要处理好它们的内部,然后用字典序将这些special string排好就可以了。
def makeLargestSpecial(self, S: str) -> str:
start = 0 # start of a special string
count = 0 # count of 1s in the current special string
res = []
for i, c in enumerate(S):
count = count + 1 if c == '1' else count - 1
if count == 0:
res.append('1'+self.makeLargestSpecial(S[start+1:i])+'0')
start = i + 1
return ''.join(sorted(res)[::-1])
726. Number of Atoms
https://leetcode.com/problems/number-of-atoms/
Given a chemical
formula
(given as a string), return the count of each atom.An atomic element always starts with an uppercase character, then zero or more lowercase letters, representing the name.
1 or more digits representing the count of that element may follow if the count is greater than 1. If the count is 1, no digits will follow. For example, H2O and H2O2 are possible, but H1O2 is impossible.
Two formulas concatenated together produce another formula. For example, H2O2He3Mg4 is also a formula.
A formula placed in parentheses, and a count (optionally added) is also a formula. For example, (H2O2) and (H2O2)3 are formulas.
Given a formula, output the count of all elements as a string in the following form: the first name (in sorted order), followed by its count (if that count is more than 1), followed by the second name (in sorted order), followed by its count (if that count is more than 1), and so on.
这道题很繁琐,但是思路很清晰,遇到括号,把括号里的东西递归求解就可以了。我的编程很面向过程,因此不倡导,在这里有一个使用栈的非常简洁的回答,可以学习学习,美化代码。
时间复杂度跟字符串长度有关,$O(S)$。
def countOfAtoms(self, formula: str) -> str:
def recursive(formula):
# if meet a formula in parentheses, count its elements recursively
count = {}
i = 0
while i < len(formula):
if i == len(formula) - 1:
if formula[-1] not in count:
count[formula[-1]] = 1
else:
count[formula[-1]] += 1
break
j = i + 1
if 'A' <= formula[i] <= 'Z':
# scan lowercase letters
while j < len(formula) and 'a' <= formula[j] <= 'z':
j += 1
element_name = formula[i:j]
# scan numbers
i = j
while j < len(formula) and '0' <= formula[j] <= '9':
j += 1
element_num = int(formula[i:j]) if j > i else 1
if element_name not in count:
count[element_name] = element_num
else:
count[element_name] += element_num
else: # meet a parentheses
parentheses_count = 1
while parentheses_count > 0:
if formula[j] == ')':
parentheses_count -= 1
if parentheses_count == 0:
break
elif formula[j] == '(':
parentheses_count += 1
j += 1
sub_count = recursive(formula[i + 1:j])
# scan numbers
j += 1
i = j
while j < len(formula) and '0' <= formula[j] <= '9':
j += 1
element_num = int(formula[i:j]) if j > 1 else 1
# combine sub_count
for k, v in sub_count.items():
if k not in count:
count[k] = element_num * sub_count[k]
else:
count[k] += element_num * sub_count[k]
i = j
return count
count = recursive(formula)
sorted_count = sorted([(k, v) for k, v in count.items()], key=lambda x: x[0])
res = ''
for each in sorted_count:
res += each[0] + (str(each[1]) if each[1] > 1 else '')
return res
698. Partition to K Equal Sum Subsets
https://leetcode.com/problems/partition-to-k-equal-sum-subsets/
Given an array of integers
nums
and a positive integerk
, find whether it’s possible to divide this array intok
non-empty subsets whose sums are all equal.
这个题,最经典的解法,居然是DFS暴力搜索。单纯这样写代码是会超时的,看了这位大神的代码,意识到可以将nums
事先排好序,再进行搜索,可以节省很多时间(因为False
的那些测试用例会更早返回)。加上了排序的代码击败了5%的Python3代码。看了一眼讨论区,有另一位大神,用非常类似的代码,击败了95% (我提交的时候是75%),原因是多了这么一段
if bucket[j] == bound:
return False
我初看时,以为是在某个数字大于桶容量时返回,但仔细想想才发现不完全是,这是因为DFS搜索是按桶从左到右放的,前若干个桶为非空桶,后几个为空桶(如果有的话)。如果用一个空桶装nums[i]
,而无法完美装下剩下的数字的话,那用下一个空桶装,也是不行的。而那些非空桶,因为排在空桶前面,早就试过了,肯定不行(不然早返回True
了),因此在bucket[j] == bound
时就能判断出这组测试用例是False
的了。
膜拜大神……
def canPartitionKSubsets(self, nums: List[int], k: int) -> bool:
sums = sum(nums)
if len(nums) < k or sums % k != 0:
return False
bound = sums//k
bucket = [bound] * k
nums.sort(reverse=True)
def dfs(i):
if i == len(nums):
return True
for j in range(k):
if nums[i] <= bucket[j]:
bucket[j] -= nums[i]
if dfs(i+1):
return True
bucket[j] += nums[i]
if bucket[j] == bound:
return False
return False
return dfs(0)
687. Longest Univalue Path
https://leetcode.com/problems/longest-univalue-path/
Given a binary tree, find the length of the longest path where each node in the path has the same value. This path may or may not pass through the root.
The length of path between two nodes is represented by the number of edges between them.
一棵树上的LUP,一共有三种可能:1)它在左子树上;2)它在右子树上;3)它穿过了根节点。前两种非常好解决,用递归就可以了。第三种情况比较麻烦,它需要计算从根节点出发,往左右子树,能去到的最远距离。这又是一个递归问题,所以又写了一个方法来处理。
class Solution:
def longestUnivaluePath(self, root: TreeNode) -> int:
if root is None:
return 0
left_longest = self.longestUnivaluePath(root.left)
right_longest = self.longestUnivaluePath(root.right)
self_longest = self.selfUnivaluePath(root.left, root.val)
+ self.selfUnivaluePath(root.right, root.val)
return max(self_longest, left_longest, right_longest)
def selfUnivaluePath(self, root: TreeNode, val) -> int:
if root is None or root.val != val:
return 0
child_longest = max(self.selfUnivaluePath(root.left, val),
self.selfUnivaluePath(root.right, val)) + 1
return child_longest