import heapq h = [] l = [(v, k) for k, v in freq.items()] for ele in l: iflen(h) < k: heapq.heappush(h, ele) else: if ele[0] > h[0][0]: heapq.heappop(h) heapq.heappush(h, ele) return [x[1] for x in h]
class Solution: def inorderTraversal(self, root: TreeNode) -> List[int]: stk, res = [root], []
while stk: cur = stk.pop() if isinstance(cur, int): res.append(cur) continue if not cur: continue stk.append(cur.right) stk.append(cur.val) stk.append(cur.left) return res
如果我们想穷举所有的子串的话,开头位置取值有 n 个可能,结尾位置取值也有 n 个可能,所以,整个搜索空间是 25 亿!所以,我们必然不能穷举。
那么怎么去设计算法呢?我们可以试试数学归纳法。
假设我们已经知道了如何求解以第 n 个字符结尾的最长子串,那么对于第 n + 1 个字符来说,怎么求解最长子串呢?如果我们知道以第 n 个字符结尾的子串,那么对于第 n + 1 个字符,有两种情况,第一,第 n + 1 个字符在最长子串中没出现过,那么,以第 n + 1 个字符为结尾的最长子串就是 substr + s[n + 1],第二种情况,第 n + 1 个字符已经出现了,那么我们把它的位置算出来 pos,然后把 pos 及以前的字符删掉,然后把第 n + 1 个字符接在后面,就形成了以 第 n+1 个字符结尾的最长子串。
怎么求整体的最长子串呢?就是遍历每个结尾位置即可。最大的那个长度就是答案。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
classSolution: deflengthOfLongestSubstring(self, s: str) -> int: @cache deflongestSubstr(idx: int) -> (str, int): if idx == 0: return s[idx], 1 sub, l = longestSubstr(idx - 1) if s[idx] notin sub: return sub + s[idx], l + 1 else: pos = sub.index(s[idx]) return sub[pos + 1:] + s[idx], l - pos ans = 0 for i inrange(len(s)): _, l = longestSubstr(i) ans = max(ans, l) return an
这个算法写出来后,我们就发现,动态规划的写法其实已经呼之欲出了。状态转移方程,就是 n 与 n - 1 之间的关系,前面也都说了:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
classSolution: deflengthOfLongestSubstring(self, s: str) -> int: curStr = "" res = 0 n = len(s)
for i inrange(n): if s[i] in curStr: pos = curStr.index(s[i]) curStr = curStr[pos + 1:] + s[i] else: curStr += s[i] res = max(res, len(curStr)) return res
classSolution: deflengthOfLongestSubstring(self, s: str) -> int: freq = set() ans = 0 n = len(s)
right = -1
for i inrange(n): if i: freq.remove(s[i - 1]) while right + 1 < n and s[right + 1] notin freq: freq.add(s[right + 1]) right += 1 ans = max(ans, right - i + 1) return ans
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,区间里面才会有元素。)