classSolution: defbuildTree(self, preorder: List[int], inorder: List[int]) -> TreeNode: n = len(preorder) pre = iter(preorder)
defrec(left: int, right: int) -> TreeNode: if left >= right: returnNone r = next(pre) m = inorder.index(r, left, right) ltree = rec(left, m) rtree = rec(m + 1, right) return TreeNode(r, ltree, rtree)
return rec(0, n)
我们声明一个函数 rec,代表 reconstruction(重建),用下标 left 和 right 来标记中序遍历序列的下标。因为前序遍历序列的分析,就是按顺序,所以我们生成一个迭代器来操作它,而下标 left 和 right,作用就是探测已到达叶子节点的标记。因为 index 这个方法的搜索范围是左闭右开区间的,所以我们对 left 和 right 的取值控制也是左闭右开的,这点需要注意。(为什么递归终止条件是 left >= right,因为是左闭右开,一个有效区间必须满足 left < right,区间里面才会有元素。)
classSolution: defcountBits(self, num: int) -> List[int]: res = [] for i inrange(num + 1): cnt = 0 n = i while n > 0: cnt += n & 1 n = n >> 1 res.append(cnt) return res
classSolution: defminMoves(self, nums: List[int]) -> int: nums.sort() res = 0 for i inreversed(range(1, len(nums))): res += nums[i] - nums[0] return res
当然了,无论你想怎么去解决这道题目,首先还是要自己看懂题目。我们看看如何确定 t 在 s 中出现的次数。举个例子,假如 s = ababc,t = abc,那么很显然,t 在 s 出现了三次。然后我们来理一下,怎么统计。首先,我们把 t 的第一个字符 a,在 s 中标定,然后,是 b 标定,最后是 c,当 a 固定在第一个字符的时候,b 其实有两个选择。这两个选择的 b 都对应着唯一的 c,所以有两次,而 a,还有另一个选择,但是选那个 a 的时候,b,c 都只有唯一选择了,所以,总次数就是 3 次。
我上面描述的这个过程,就是一种最直观的统计策略。逐个标定 t 的每个字符,而且要穷尽每个字符的多个位置选择。这个就是一个比较明显的回溯过程。所以,很自然,我写出了回溯算法:
# 缓存 k = (p, q) v = self.mem.get(k, -1) if v > 0: return v if q == self.n - 1: v = self.s[p:].count(self.t[-1]) self.mem[k] = v return v total = 0 for i inrange(p, self.m): ifself.m - i >= self.n - q andself.s[i] == self.t[q]: total += self.dfs(i + 1, q + 1) self.mem[k] = total return total
对于这道手表的题目,题目要求点亮 num 盏灯,然后计算可能的时间数量。所以,我们可以把所有 num 盏灯都点亮,作为达成了目标的条件。第二,手表上,有代表小时的灯,1,2,4,8,四盏,以及代表分钟的灯1,2,4…… 等六盏灯,其实就相当于一共有 10 个位置,每次可以点亮一个。这么看,总状态空间,也就 2^10 个,就算都遍历了,也没多少……第三,我们要保存的状态,就是,点到第 i 盏灯的时候,表盘上所有的灯的亮灭情况。可以开动了: