diff --git a/coding2.md b/coding2.md new file mode 100644 index 0000000..d284c90 --- /dev/null +++ b/coding2.md @@ -0,0 +1,2536 @@ + + +# 栈 +```python +# 232.用栈实现队列 +class MyQueue: + def __init__(self): + self.stack_in = [] + self.stack_out = [] + def push(self, x: int) -> None: + self.stack_in.append(x) + def pop(self) -> int: + if self.empty(): + return None + if self.stack_out: + return self.stack_out.pop() + else: + for i in range(len(self.stack_in)): + self.stack_out.append(self.stack_in.pop()) + return self.stack_out.pop() + def peek(self) -> int: + ans = self.pop() + self.stack_out.append(ans) + return ans + def empty(self) -> bool: + return not (self.stack_in or self.stack_out) +``` + +```python +# 225. 用队列实现栈 +class MyStack: + def __init__(self): + self.queue1 = collections.deque() + self.queue2 = collections.deque() + + def push(self, x: int) -> None: + self.queue2.append(x) + while self.queue1: + self.queue2.append(self.queue1.popleft()) + self.queue1, self.queue2 = self.queue2, self.queue1 + + def pop(self) -> int: + return self.queue1.popleft() + def top(self) -> int: + return self.queue1[0] + def empty(self) -> bool: + return not self.queue1 +``` + +```python +# 20. 有效的括号 +class Solution: + def isValid(self, s: str) -> bool: + stack = [] + for item in s: + if item == '(': + stack.append(')') + elif item == '[': + stack.append(']') + elif item == '{': + stack.append('}') + elif not stack or stack[-1] != item: + return False + else: + stack.pop() + return True if not stack else False +``` + +```python +# 1047 删除字符串中的所有相邻重复项 +class Solution: + def removeDuplicates(self, s: str) -> str: + res = list() + for item in s: + if res and res[-1] == item: + res.pop() + else: + res.append(item) + return "".join(res) # 字符串拼接 +``` + + +```python +# 150. 逆波兰表达式求值 +from operator import add, sub, mul +class Solution: + op_map = {'+': add, '-': sub, '*': mul, '/': lambda x, y: int(x / y)} + def evalRPN(self, tokens: List[str]) -> int: + stack = [] + for token in tokens: + if token not in {'+', '-', '*', '/'}: + stack.append(int(token)) + else: + op2 = stack.pop() + op1 = stack.pop() + stack.append(self.op_map[token](op1, op2)) # 第一个出来的在运算符后面 + return stack.pop() +``` + +```python +# 239. 滑动窗口最大值 +from collections import deque +class MyQueue: #单调队列(从大到小 + def __init__(self): + self.queue = deque() #这里需要使用deque实现单调队列,直接使用list会超时 + #每次弹出的时候,比较当前要弹出的数值是否等于队列出口元素的数值,如果相等则弹出。同时pop之前判断队列当前是否为空。 + def pop(self, value): + if self.queue and value == self.queue[0]: + self.queue.popleft() #list.pop()时间复杂度为O(n),这里需要使用collections.deque(),保持队列里的数值从大到小。 + def push(self, value): + while self.queue and value > self.queue[-1]: + self.queue.pop() + self.queue.append(value) + def front(self): + return self.queue[0] +class Solution: + def maxSlidingWindow(self, nums: List[int], k: int) -> List[int]: + que = MyQueue() + result = [] + for i in range(k): #先将前k的元素放进队列 + que.push(nums[i]) + result.append(que.front()) #result 记录前k的元素的最大值 + for i in range(k, len(nums)): + que.pop(nums[i - k]) #滑动窗口移除最前面元素 + que.push(nums[i]) #滑动窗口前加入最后面的元素 + result.append(que.front()) #记录对应的最大值 + return result +``` +```python +#R 0 Decode String/字符串解码 (Medium) "3[a]2[bc]",输出:"aaabcbc" +class Solution: + def decodeString(self, s: str) -> str: + stack, res, multi = [], "", 0 + for c in s: + if c == '[': + stack.append([multi, res]) + res, multi = "", 0 + elif c == ']': + cur_multi, last_res = stack.pop() + res = last_res + cur_multi * res + elif '0' <= c <= '9': + multi = multi * 10 + int(c) + else: + res += c + return res +``` +```python +#R 227 Basic Calculator II/基本计算器 II (Medium) +# 输入:s = "3+2*2" 输出:7。 当前元素为符号时,更新完栈后要记得更新数字和符号 +class Solution: + def calculate(self, s: str) -> int: + stack = [] + num = 0; sign = '+' + for i in range(len(s)): + if s[i].isdigit(): + num = num*10 + int(s[i]) + if s[i] in '+-*/' or i == len(s)-1: + if sign == '+': + stack.append(num) + elif sign == '-': + stack.append(-num) + elif sign == '*': + stack.append(stack.pop() * num) + else: + stack.append(int(stack.pop() / num)) + num = 0; sign = s[i] + return sum(stack) +``` +```python +#H 385 Mini Parser/迷你语法分析器 (Medium) +# [123,[456,[789]]] [1,[2]] +class Solution: + def deserialize(self, s: str) -> NestedInteger: + if s[0] != '[': + return NestedInteger(int(s)) + stk = [] + num, negative = 0, False + for i in range(len(s)): + if s[i] == '[': + stk.append(NestedInteger()) + elif s[i] == '-': + negative = True + elif s[i].isdigit(): + num = num * 10 + int(s[i]) + elif s[i] == ',' or s[i] == ']': + if s[i - 1].isdigit(): + if negative: + num *= -1 + stk[-1].add(NestedInteger(num)) + num, negative = 0, False + if s[i] == ']' and len(stk) > 1: + ni = stk.pop() + stk[-1].add(ni) + return stk.pop() +``` + + +```python +#R 636 Exclusive Time of Functions/函数的独占时间 (Medium) +# 当函数调用开始时,如果当前有函数正在运行,则当前正在运行函数应当停止,此时计算其的执行时间,然后将调用函数入栈。 +# 当函数调用结束时,将栈顶元素弹出,并计算相应的执行时间,如果此时栈顶有被暂停的函数,则开始运行该函数。 +# 输入:n = 2, logs = ["0:start:0","1:start:2","1:end:5","0:end:6"] 输出:两个函数的独占时间分别为[3,4] +class Solution: + def exclusiveTime(self, n: int, logs: List[str]) -> List[int]: + ans = [0] * n + st = [] + for log in logs: + idx, tp, timestamp = log.split(':') + idx, timestamp = int(idx), int(timestamp) + if tp[0] == 's': + if st: + ans[st[-1][0]] += timestamp - st[-1][1] + st[-1][1] = timestamp + st.append([idx, timestamp]) + else: + i, t = st.pop() + ans[i] += timestamp - t + 1 + if st: + st[-1][1] = timestamp + 1 + return ans +``` + +```python +#R 921 Minimum Add to Make Parentheses Valid/使括号有效的最少添加 (Medium) +class Solution: + def minAddToMakeValid(self, s: str) -> int: + stk = [] + for c in s: + if c == ')' and stk and stk[-1] == '(': + stk.pop() + else: + stk.append(c) + return len(stk) +``` + +# 单调栈 +https://blog.csdn.net/zy_dreamer/article/details/131036101 +从左到右遍历元素。单调递增栈:栈顶最小。 +- 查找 「比当前元素大的元素」 就用 单调递增栈,查找 「比当前元素小的元素」 就用 单调递减栈。 +- 从 「左侧」 查找就看 「插入栈」 时的栈顶元素,从 「右侧」 查找就看 「弹出栈」 时即将插入的元素。 +```python +#H 模板,单调递增栈(递减改为小于等于) +def monotoneIncreasingStack(nums): + stack = [] + for num in nums: + while stack and num >= stack[-1]: + stack.pop() + stack.append(num) +``` + +```python +#R 0 每日温度,观测到更高的气温,至少需要等待的天数 +class Solution: + def dailyTemperatures(self, T: List[int]) -> List[int]: + n = len(T) + stack = [] + ans = [0 for _ in range(n)] + for i in range(n): + while stack and T[i] > T[stack[-1]]: + index = stack.pop() + ans[index] = (i-index) + stack.append(i) + return ans +``` + +```python +#H 0 接雨水,排列的柱子,下雨之后能接多少雨水。 +# 方法总结:找上一个更大元素,在找的过程中填坑。while中加了等号,这可以让栈中没有重复元素,以节省空间。 https://www.bilibili.com/video/BV1VN411J7S7/?vd_source=3f76269c2962e01f0d61bbeac282e5d2 单调递减栈。 +class Solution: + def trap(self, height: List[int]) -> int: + ans = 0 + st = [] + for i, h in enumerate(height): + while st and h >= height[st[-1]]: + bottom_h = height[st.pop()] + if not st: # len(st) == 0 + break + left = st[-1] + dh = min(height[left], h) - bottom_h # 面积的高 + ans += dh * (i - left - 1) + st.append(i) + return ans +``` + + +```python +#R 0.下一个更大元素 I, 每一nums1中的x在nums2中对应位置右侧的第一个比 x 大的元素。如果不存在 -1 +class Solution: + def nextGreaterElement(self, nums1: List[int], nums2: List[int]) -> List[int]: + res = {} + stack = [] + for num in reversed(nums2): + while stack and num >= stack[-1]: + stack.pop() + res[num] = stack[-1] if stack else -1 + stack.append(num) + return [res[num] for num in nums1] +``` + +```python +#H 0 柱状图中最大的矩形。n个非负整数,用来表示柱状图中各个柱子的高度。求该柱状图中勾勒出来的矩形的最大面积。 +# 在i左侧的小于h的最近元素的下标left,右侧的小于h的最近元素的下标right,矩形的宽度就是right−left−1 +class Solution: + def largestRectangleArea(self, heights: List[int]) -> int: + n, heights, st, ans = len(heights), [0] + heights + [0], [], 0 + for i in range(n + 2): + while st and heights[st[-1]] > heights[i]: + ans = max(ans, heights[st.pop(-1)] * (i - st[-1] - 1)) + st.append(i) + return ans +``` +```python +#H 85 Maximal Rectangle/最大矩形 (Hard) +class Solution: + def maximalRectangle(self, matrix: List[List[str]]) -> int: + m = len(matrix) + if m == 0: return 0 + n = len(matrix[0]) + heights = [0] * n + ans = 0 + for i in range(m): + for j in range(n): + if matrix[i][j] == "0": + heights[j] = 0 + else: + heights[j] += 1 + ans = max(ans, self.largestRectangleArea(heights)) + return ans +``` + +```python +#H 315. 计算右侧小于当前元素的个数 困难 +# counts[i] 的值是 nums[i] 右侧小于 nums[i] 的元素的数量。输入数组反过来插入一个有序数组(降序)中,插入的位置就是在原数组中位于它右侧的元素的个数。 +class Solution: + def countSmaller(self, nums: List[int]) -> List[int]: + sortns = [] + res = [] + for n in reversed(nums): + idx = bisect.bisect_left(sortns, n) + res.append(idx) + sortns.insert(idx,n) + return res[::-1] +``` +```python +#R 找前面比自己小的元素 +def find_smaller_elements(nums): + result = [] + stack = [] # 用于存储元素的值 + for num in nums: + while stack and stack[-1] >= num: + stack.pop() + if not stack: + result.append(None) # 如果前面没有比当前元素小的元素,可以用 None 表示 + else: + result.append(stack[-1]) + stack.append(num) + return result +``` + +```python +#R 503 Next Greater Element II/下一个更大元素 II (Medium) +class Solution: + def nextGreaterElements(self, nums: List[int]) -> List[int]: + n = len(nums) + ans = [-1] * n + stack = [] + for i in range(n * 2 - 1): + while stack and nums[i % n] > nums[stack[-1]]: + ans[stack.pop()] = nums[i % n] + stack.append(i % n) + return ans +``` +```python +#R 768 Max Chunks To Make Sorted II/最多能完成排序的块 II (Hard) +# 分割成若干块 ,分别进行排序。之后再连接起来,使得连接的结果和按升序排序后的原数组相同。 +# 从左到右,每个分块都有一个最大值,并且这些分块的最大值呈单调递增(非严格递增)321 65 +class Solution: + def maxChunksToSorted(self, arr: List[int]) -> int: + stk = [] + for v in arr: + if not stk or v >= stk[-1]: + stk.append(v) + else: + mx = stk.pop() + while stk and stk[-1] > v: + stk.pop() + stk.append(mx) + return len(stk) +``` + + + +# 堆 + +```python +# 346.99 前 K 个高频元素 +#优先级队列=披着队列外衣的堆 时间复杂度:O(nlogk) 空间复杂度:O(n) +import heapq +class Solution: + def topKFrequent(self, nums: List[int], k: int) -> List[int]: + #要统计元素出现频率 + map_ = {} #nums[i]:对应出现的次数 + for i in range(len(nums)): + map_[nums[i]] = map_.get(nums[i], 0) + 1 + #对频率排序 + #定义一个小顶堆,大小为k + pri_que = [] #小顶堆 + #用固定大小为k的小顶堆,扫描所有频率的数值 + for key, freq in map_.items(): + heapq.heappush(pri_que, (freq, key)) + if len(pri_que) > k: #如果堆的大小大于了K,则队列弹出,保证堆的大小一直为k + heapq.heappop(pri_que) + #找出前K个高频元素,因为小顶堆先弹出的是最小的,所以倒序来输出到数组 + result = [0] * k + for i in range(k-1, -1, -1): + result[i] = heapq.heappop(pri_que)[1] + return result +``` + +```python +# 23 Merge k Sorted Lists/合并 K 个升序链表 (Hard) +class Solution: + def mergeKLists(self, lists: List[Optional[ListNode]]) -> Optional[ListNode]: + setattr(ListNode, "__lt__", lambda a, b: a.val < b.val) + pq = [head for head in lists if head] + heapify(pq) + dummy = cur = ListNode() + while pq: + node = heappop(pq) + if node.next: + heappush(pq, node.next) + cur.next = node + cur = cur.next + return dummy.next +``` +```python +# 295 Find Median from Data Stream/数据流的中位数 (Hard) +# 建立一个小顶堆A和大顶堆B ,各保存列表的一半元素 +from heapq import * +class MedianFinder: + def __init__(self): + self.A = [] # 小顶堆,保存较大的一半 + self.B = [] # 大顶堆,保存较小的一半 + def addNum(self, num: int) -> None: + if len(self.A) != len(self.B): + heappush(self.A, num) + heappush(self.B, -heappop(self.A)) + else: + heappush(self.B, -num) + heappush(self.A, -heappop(self.B)) + def findMedian(self) -> float: + return self.A[0] if len(self.A) != len(self.B) else (self.A[0] - self.B[0]) / 2.0 +``` +```python +#R 502 IPO (Hard) +# 贪心 + 排序 + 大顶堆:选择当前可启动的最大利润项目 +# 利润profits,启动该项目需要的最小资本capital。最初你的资本为w,选择最多k个项目 +class Solution: + def findMaximizedCapital(self, k: int, w: int, profits: List[int], capital: List[int]) -> int: + # 生成索引序列 并 根据资本值对索引进行升序排序 + n = len(capital) + indexes = sorted( [i for i in range(n)], key=lambda i: capital[i]) + pq = [] # 维护堆内利润值的大顶堆 + i = 0 + while k > 0: + # 将启动资本小于等于当前资本的项目的利润加入大顶堆 + while i < n and capital[indexes[i]] <= w: + heapq.heappush(pq, -profits[indexes[i]]) # 取相反数实现大顶堆 + i += 1 + if not pq: break # 没有可以启动的项目,后面启动资本更大的项目也无法启动,退出 + w += -heappop(pq) # 选择启动资本满足条件的项目中利润最大的那个,更新w + k -= 1 + return w +``` +```python +# 1046 Last Stone Weight/? (Easy) +# 从中选出两块 最重的 石头,然后将它们一起粉碎 y-x +class Solution: + def lastStoneWeight(self, stones: List[int]) -> int: + # 初始化 + heap = [-stone for stone in stones] + heapq.heapify(heap) + # 模拟 + while len(heap) > 1: + x,y = heapq.heappop(heap),heapq.heappop(heap) + if x != y: + heapq.heappush(heap,x-y) + if heap: return -heap[0] + return 0 +``` +```python +# 1352.9 最多可以参加的会议数目 (Medium) +# events[i] = [startDayi, endDayi] ,表示会议 i 开始于 startDayi ,结束于 endDayi +# 贪心:对于某一天i,参加满足条件的最早结束的会议。 +class Solution: + def maxEvents(self, events: List[List[int]]) -> int: + ans = 0 + end = list() + events = sorted(events,reverse=True) + for i in range(1,100010,1): + while events and events[-1][0] == i: #加入这个时刻刚开始会议 + heapq.heappush(end, events.pop()[1]) + while end and end[0] < i: # 删除不能参加的会议,在这个时刻会议结束了 + heapq.heappop(end) + if end: + heapq.heappop(end) + ans += 1 + return ans +``` +```python +# 数字合并,可以选择加法或者乘法,每次合并的代价是两个数字的和或者积,使得最终的代价最小。 +import heapq +def greedy_merge(numbers): + # 将数字转化为负数,构建最小堆 + heap = [-num for num in numbers] + heapq.heapify(heap) + while len(heap) > 1: + # 从堆中取出两个最小的数字 + num1 = -heapq.heappop(heap) + num2 = -heapq.heappop(heap) + # 合并两个数字,可以根据需要选择加法或者乘法 + merged_num = num1 + num2 # 或者 merged_num = num1 * num2 + # 将合并后的数字放回堆中 + heapq.heappush(heap, -merged_num) + # 最终堆中剩下的数字即为合并后的结果 + result = -heap[0] + return result +``` +```python +# 253.会议室 +import heapq +def minMeetingRooms(intervals): + if not intervals: + return 0 + # 按照会议的起始时间进行排序 + intervals.sort(key=lambda x: x[0]) + # 使用最小堆来存储会议的结束时间 + end_times = [] + heapq.heappush(end_times, intervals[0][1]) + # 遍历会议,检查是否有空闲的会议室,如果有则更新结束时间,如果没有则添加新的会议室 + for i in range(1, len(intervals)): + if intervals[i][0] >= end_times[0]: + heapq.heappop(end_times) + heapq.heappush(end_times, intervals[i][1]) + # 最终堆的大小即为所需的最小会议室数量 + return len(end_times) +``` + + + +# 排序 + +* 冒泡排序(Bubble Sort): +时间复杂度:平均情况 O(n^2),最坏情况 O(n^2),最好情况 O(n)(如果已经有序);空间复杂度:O(1)(原地排序)。 +* 选择排序(Selection Sort): +时间复杂度:平均情况 O(n^2),最坏情况 O(n^2),最好情况 O(n^2);空间复杂度:O(1)(原地排序)。 +* 插入排序(Insertion Sort): +#时间复杂度:平均情况 O(n^2),最坏情况 O(n^2),最好情况 O(n)(如果已经有序); 空间复杂度:O(1)(原地排序)。 +* 归并排序(Merge Sort): +时间复杂度:平均情况 O(n log n),最好情况 O(n log n),最坏情况 O(n log n); 空间复杂度:O(n)(需要额外的空间来合并子数组)。 +* 快速排序(Quick Sort): +时间复杂度:平均情况 O(n log n),最好情况 O(n log n),最坏情况 O(n^2)(取决于选取的主元); 空间复杂度:O(log n)(递归调用栈的深度)。 +* 堆排序(Heap Sort): +#时间复杂度:平均情况 O(n log n),最好情况 O(n log n),最坏情况 O(n log n); 空间复杂度:O(1)(原地排序)。 + +```python +def bubble_sort(arr): + n = len(arr) + for i in range(n): + for j in range(0, n-i-1): + if arr[j] > arr[j+1]: + arr[j], arr[j+1] = arr[j+1], arr[j] +def insertion_sort(arr): + for i in range(1, len(arr)): + key = arr[i] + j = i - 1 + while j >= 0 and key < arr[j]: + arr[j + 1] = arr[j] + j -= 1 + arr[j + 1] = key +def selection_sort(arr): + n = len(arr) + for i in range(n): + min_index = i + for j in range(i + 1, n): + if arr[j] < arr[min_index]: + min_index = j + arr[i], arr[min_index] = arr[min_index], arr[i] +``` + +```python +def merge_sort(arr): + if len(arr) > 1: + mid = len(arr) // 2 + left_half = arr[:mid] + right_half = arr[mid:] + # 递归地对左右两部分进行归并排序 + merge_sort(left_half) + merge_sort(right_half) + i = j = k = 0 + # 合并两个有序子数组 + while i < len(left_half) and j < len(right_half): + if left_half[i] < right_half[j]: + arr[k] = left_half[i] + i += 1 + else: + arr[k] = right_half[j] + j += 1 + k += 1 + # 处理剩余的元素 + while i < len(left_half): + arr[k] = left_half[i] + i += 1 + k += 1 + while j < len(right_half): + arr[k] = right_half[j] + j += 1 + k += 1 +``` + +```python +#R 快速排序 +def quick_sort(arr, low=0, high=None): + if high is None: + high = len(arr) - 1 + + def partition(arr, low, high): + pivot = arr[high] + i = low - 1 + for j in range(low, high): + # 如果当前元素小于等于基准 + if arr[j] <= pivot: + # 交换元素 + i += 1 + arr[i], arr[j] = arr[j], arr[i] + # 将基准放到正确位置 + arr[i + 1], arr[high] = arr[high], arr[i + 1] + return i + 1 + + def _quick_sort(arr, low, high): + if low < high: + partition_index = partition(arr, low, high) + _quick_sort(arr, low, partition_index - 1) + _quick_sort(arr, partition_index + 1, high) + + _quick_sort(arr, low, high) + return arr +``` + +```python +# BST排序 +class TreeNode: + def __init__(self, value): + self.value = value + self.left = None + self.right = None +def insert(root, value): + if not root: + return TreeNode(value) + if value < root.value: + root.left = insert(root.left, value) + else: + root.right = insert(root.right, value) + return root +def in_order_traversal(root, result): + if root: + in_order_traversal(root.left, result) + result.append(root.value) + in_order_traversal(root.right, result) +def bst_sort(nums): + root = None + for num in nums: + root = insert(root, num) + result = [] + in_order_traversal(root, result) + return result +``` + +```python +#R 堆排序 https://blog.csdn.net/Solititude/article/details/129182217 +def heapify(arr, n, i): + largest = i + left_child = 2 * i + 1 + right_child = 2 * i + 2 + if left_child < n and arr[left_child] > arr[largest]: + largest = left_child + if right_child < n and arr[right_child] > arr[largest]: + largest = right_child + if largest != i: + arr[i], arr[largest] = arr[largest], arr[i] + heapify(arr, n, largest) +def heap_sort(arr): + n = len(arr) + # 构建最大堆 + for i in range(n // 2 - 1, -1, -1): + heapify(arr, n, i) + # 逐步取出堆顶元素,进行堆排序 + for i in range(n - 1, 0, -1): + arr[0], arr[i] = arr[i], arr[0] + heapify(arr, i, 0) +``` + +```python +# 0. sort a nearly sorted array: 对每个元素离正确位置不超过K的数列排序。用insert排序,O(NK),可以二分法只搜索邻近K个(NlogK)或者直接建立k element min heap,每次移除root(min), 加入新元素再heapify. +import heapq +def sortNearlySortedArray(nums, k): + # 创建一个最小堆 + min_heap = nums[:k] + heapq.heapify(min_heap) + result = [] + for i in range(k, len(nums)): + min_element = heapq.heappop(min_heap) + result.append(min_element) + heapq.heappush(min_heap, nums[i]) + # 将堆中剩余的元素取出并加入结果数组 + while min_heap: + min_element = heapq.heappop(min_heap) + result.append(min_element) + return result +``` + + +# 随机 + +```python +# 0 水塘抽样(Reservoir Sampling),第i步以1/i的概率选取nums[i],默认选取前k个,第i步等概率产生[1,i]中的一个数r,如果r<=k 则将selected[r] 替换成 nums[i] +import random +def reservoir_sampling_single(nums): + selected = nums[0] + for i in range(2, len(nums) + 1): + # 以 1/i 的概率选取 nums[i-1] + if random.randint(1, i) == i: + selected = nums[i - 1] + return selected +def reservoir_sampling_k(nums, k): + selected = nums[:k] + for i in range(k + 1, len(nums) + 1): + # 等概率产生 [1, i] 中的一个数 r + p = random.randint(1, i) + # 如果 r <= k,则将 selected[r-1] 替换为 nums[i-1] + if p <= k: + selected[p - 1] = nums[i - 1] + return selected +``` + +```python +# 0 按权重随机选择 +import random +class Solution: + def __init__(self, w): + self.cumulative_weights = [0] * len(w) + self.cumulative_weights[0] = w[0] + for i in range(1, len(w)): + self.cumulative_weights[i] = self.cumulative_weights[i-1] + w[i] + self.sum_weights = sum(w) + def pickIndex(self) -> int: + target = random.randint(1, self.sum_weights) + # 二分查找 + left, right = 0, len(self.cumulative_weights) - 1 + while left < right: + mid = (left + right) // 2 + if self.cumulative_weights[mid] < target: + left = mid + 1 + else: + right = mid + return left +``` + + +```python +# 随机产生数字没有重复 or 打乱数组 +def generate_unique_numbers(minimum, maximum): + # 初始化数组,包含[min, max]范围内的所有数字 + nums = list(range(minimum, maximum + 1)) + result = [] + while nums: + # 生成 [0, len(nums)-1] 之间的随机索引 + rand_index = random.randint(0, len(nums) - 1) + # 交换当前随机选中的数字和数组末尾的数字 + nums[rand_index], nums[-1] = nums[-1], nums[rand_index] + # 将末尾数字添加到结果中,并从数组中移除 + result.append(nums.pop()) + return result +``` + +```python +# Fisher-Yates 洗牌算法(也称为 Knuth shuffle) +class Solution: + def __init__(self, nums): + self.original_nums = nums[:] + self.shuffle_nums = nums[:] + def reset(self): + # 重置为原始数组 + self.shuffle_nums = self.original_nums[:] + return self.shuffle_nums + def shuffle(self): + n = len(self.shuffle_nums) + for i in range(n): + # 生成 [i, n-1] 之间的随机索引 + rand_index = random.randint(i, n - 1) + # 交换当前随机选中的数字和数组中第 i 个位置的数字 + self.shuffle_nums[i], self.shuffle_nums[rand_index] = self.shuffle_nums[rand_index], self.shuffle_nums[i] + return self.shuffle_nums +``` + + +```python +# 470. Implement Rand10() Using Rand7() +def Rand10(): + while True: + # 生成 [1, 49] 范围内的随机数 + a = Rand7() + b = Rand7() + val = (a - 1) * 7 + b + + # 拒绝采样,重新生成直到得到 [1, 40] 范围内的数 + if val <= 40: + return val % 10 + 1 +``` + +```python +# 380 Insert Delete GetRandom O(1)/O(1) 时间插入、删除和获取随机元素 (Medium) +class RandomizedSet: + def __init__(self): + self.vals = [] + self.idxs = {} + def insert(self, val: int) -> bool: + if val in self.vals: + return False + self.idxs[val] = len(self.vals) + self.vals.append(val) + return True + def remove(self, val: int) -> bool: + if val not in self.vals: + return False + idx = self.idxs[val] + self.vals[idx] = self.vals[-1] + self.idxs[self.vals[idx]] = idx + self.vals.pop() + del self.idxs[val] + return True + def getRandom(self) -> int: + return random.choice(self.vals) +``` + +```python +#R 709.99 Random Pick with Blacklist/黑名单中的随机数 (Hard) +# 选取一个 未加入 黑名单 blacklist 的整数 +# 构建一个从 [0,n−m) 范围内的黑名单数到 [n−m,n) 的白名单数的映射 +class Solution: + def __init__(self, n: int, blacklist: List[int]): + m = len(blacklist) + self.bound = w = n - m + black = {b for b in blacklist if b >= self.bound} + self.b2w = {} + for b in blacklist: + if b < self.bound: + while w in black: + w += 1 + self.b2w[b] = w + w += 1 + def pick(self) -> int: + x = randrange(self.bound) + return self.b2w.get(x, x) +``` + +# 累加和 + +```python +#325. Maximum Size Subarray Sum Equals k +def maxSubArrayLen(nums, k): + # 初始化累加和为0,结果为0 + sum_val = 0 + res = 0 + # 使用字典记录累加和及其对应的下标 + # 初始时,将累加和为0的下标设为-1 + sum_index_map = {0: -1} + for i in range(len(nums)): + # 计算当前位置的累加和 + sum_val += nums[i] + # 如果当前累加和减去目标值 k 在字典中,说明找到了符合条件的子数组 + if sum_val - k in sum_index_map: + res = max(res, i - sum_index_map[sum_val - k]) + # 如果当前累加和不在字典中,将其加入字典 + if sum_val not in sum_index_map: + sum_index_map[sum_val] = i + return res +``` + +```python +#R 862. Shortest Subarray with Sum at Least K +import heapq +def shortestSubarray(nums, k): + # 初始化结果为正无穷大,累加和为0,优先队列用于保存累加和及其对应的下标 + res = float('inf') + sum_val = 0 + pq = [(0, -1)] + for i in range(len(nums)): + # 计算当前位置的累加和 + sum_val += nums[i] + # 检查队列中是否有满足条件的累加和,如果有,更新结果 + while pq and sum_val - pq[0][0] >= k: + res = min(res, i - pq[0][1]) + heapq.heappop(pq) + # 将当前累加和及其下标加入队列 + heapq.heappush(pq, (sum_val, i)) + # 如果结果仍然是正无穷大,说明没有符合条件的子数组 + return -1 if res == float('inf') else res +``` + +```python +# 525. Contiguous Array, 给定一个二进制数组, 找到含有相同数量的 0 和 1 的最长连续子数组,返回该子数组的长度。 +# 遍历数组时,对于每个元素,如果是 1 就加一,如果是 0 就减一,然后将当前累加和和对应的下标加入字典中。如果相同的累加和出现多次,说明两个下标之间的 0 和 1 的个数是相等的。 +def findMaxLength(nums): + # 初始化累加和为0,用字典记录每个累加和对应的下标列表 + sum_map = {0: [-1]} + sum_val = 0 + # 遍历数组,计算累加和,并记录每个累加和对应的下标 + for i in range(len(nums)): + sum_val += 1 if nums[i] == 1 else -1 + if sum_val not in sum_map: + sum_map[sum_val] = [i] + else: + sum_map[sum_val].append(i) + # 遍历字典,找出最长子数组的长度 + max_length = 0 + for indices in sum_map.values(): + max_length = max(max_length, indices[-1] - indices[0]) + return max_length +``` + + +```python +# 有k个1的子列个数(map value = vector)/最长(value = 第一个出现index)/最短(最新一个index)子列都可以用累计和。 +# 如果是不超过k个1的最长子列也可以用累加和,最长子列一定是k个1,不然就是全列。但是如果是计算不超过k个1的子列个数,则只能用移动窗口了(可以发现凡是求不等式子列个数,就必须用移动窗口) +0. 有 k 个 1 的子列个数,这个问题可以通过维护一个累加和,以及使用一个字典来记录每个累加和出现的频次。具体步骤如下: +def countSubarraysWithKOnes(nums, k): + # 初始化累加和为0,字典记录每个累加和出现的频次 + sum_val = 0 + count_map = {0: 1} + count = 0 + # 遍历数组,计算累加和和累加和为 sum_val - k 的频次 + for num in nums: + sum_val += num + if sum_val - k in count_map: + count += count_map[sum_val - k] + count_map[sum_val] = count_map.get(sum_val, 0) + 1 + return count +``` + +```python +# 0 最长子列:由 0 和 1 组成的数组中找到最短的连续子列,该子列中 1 的个数等于给定的 k。这个问题可以通过维护一个累加和,并使用一个字典来记录每个累加和第一次出现的位置。 +def longestSubarrayWithKOnes(nums, k): + # 初始化累加和为0,字典记录每个累加和第一次出现的位置 + sum_val = 0 + index_map = {0: -1} + max_length = 0 + # 遍历数组,更新最长子列长度 + for i, num in enumerate(nums): + sum_val += num + if sum_val not in index_map: + index_map[sum_val] = i + if sum_val - k in index_map: + max_length = max(max_length, i - index_map[sum_val - k]) + return max_length +``` + + +```python +# 0 Find the Longest Substring Containing Vowels in Even Counts # 返回最长子串的长度:每个元音字母,在子字符串中都恰好出现了偶数次。 +# 每当遇到一个元音字母时,更新状态 num,然后通过字典 res_dict 记录每个状态第一次出现的位置。如果相同的状态再次出现,说明中间的子列元音个数是偶数 +def findTheLongestSubstring(s): + # 初始化字典,用于记录元音字母和对应的二进制位置 + vowels_dict = {'a': 0, 'e': 1, 'i': 2, 'o': 3, 'u': 4} + # 初始化状态 num 为0,表示当前状态的二进制数 + num = 0 + # 初始化结果为0,用于记录最长子列的长度 + res = 0 + # 使用字典 resDict 记录每个状态第一次出现的位置 + res_dict = {0: -1} + # 遍历字符串 + for i in range(len(s)): + # 如果当前字符是元音字母,更新状态 num + if s[i] in vowels_dict: + num ^= (1 << vowels_dict[s[i]]) + # 如果当前状态已经出现过,更新最长子列长度 + if num in res_dict: + res = max(res, i - res_dict[num]) + else: + # 如果当前状态第一次出现,记录当前位置 + res_dict[num] = i + return res +``` + +```python +# 532. 数组中的 k-diff 数对 +class Solution: + def findPairs(self, nums: List[int], k: int) -> int: + visited, res = set(), set() + for num in nums: + if num - k in visited: + res.add(num - k) + if num + k in visited: + res.add(num) + visited.add(num) + return len(res) +``` +```python +# 370.给定一个长度为n的数组,初始时所有元素均为0。对该数组进行k次操作,每次操作将某一范围的元素加上一个特定的值。返回经过k次操作后得到的数组。 +# 初始都是0的array上,[a,b,k]指在index [a,b]之间都加上k. 若干组pair以后问最终输出:对指标a加上k, b+1 减去k. 这样对0到n的累加和就是最终输出。 +def getModifiedArray(length, updates): + result = [0] * length + # 构建差分数组 + for update in updates: + start, end, inc = update + result[start] += inc + if end + 1 < length: + result[end + 1] -= inc + # 根据差分数组还原原数组 + for i in range(1, length): + result[i] += result[i - 1] + return result +``` +```python +# 238 Product of Array Except Self/除自身以外数组的乘积 (Medium) +class Solution: + def productExceptSelf(self, nums: List[int]) -> List[int]: + n = len(nums) + pre = [1] * n + for i in range(1, n): + pre[i] = pre[i - 1] * nums[i - 1] + + suf = [1] * n + for i in range(n - 2, -1, -1): + suf[i] = suf[i + 1] * nums[i + 1] + + return [p * s for p, s in zip(pre, suf)] +``` +```python +# 523 Continuous Subarray Sum/连续的子数组和 (Medium) +# 好的子数组是:长度至少为 2 ,且子数组元素总和为 k 的倍数。有没有好的子数组。方法前缀和 +class Solution: + def checkSubarraySum(self, nums, k): + d = {0: -1} + pre = 0 + for index, num in enumerate(nums): + pre += num + rem = pre % k + i = d.get(rem, index) + if i == index: + d[rem] = index + elif i <= index - 2: + return True + return False +``` +```python +# 1109 Corporate Flight Bookings/? (Medium) +# [firsti, lasti, seatsi] 意味着在从闭区间 firsti 到 lasti 的 每个航班 上预订了 seatsi 个座位。 +class Solution: + def corpFlightBookings(self, bookings: List[List[int]], n: int) -> List[int]: + diff = [0] * n + for t in range(len(bookings)): + i, j, temp = bookings[t] + diff[i-1] += temp + if j < n: # 越界情况特殊判断 + diff[j] -= temp + for i in range(1, len(diff)): + diff[i] = diff[i] + diff[i-1] + return diff +``` +```python +# 1124 Longest Well-Performing Interval/表现良好的最长时间段 (Medium) +# 员工每天的工作小时数。返回「表现良好时间段」(=「劳累的天数」是严格 大于「不劳累的天数」)的最大长度。 +# 前缀和 + 哈希表 +class Solution: + def longestWPI(self, hours: List[int]) -> int: + ans = s = 0 + pos = {} + for i, x in enumerate(hours): + s += 1 if x > 8 else -1 + if s > 0: + ans = i + 1 + elif s - 1 in pos: + ans = max(ans, i - pos[s - 1]) + if s not in pos: + pos[s] = i + return ans +``` + + + + + + + +# 二进制 + +```python +# 问题2: 正数int转化为2进制 +def int_to_binary(n): + res = "" + while n: + res = str(n % 2) + res + n //= 2 + return res if res else "0" +# 问题2: 负数int转化为2进制 +def negative_int_to_binary(n): + n = -n-1 + res = "" + for i in range(16): + res = str(1 - (n % 2)) + res + n //= 2 + return res +``` + +```python +# 问题3: 遍历n的每一位 +# 0. Convert a Number to Hexadecimal +class Solution(object): + def toHex(self, num): + # -N和 16 ** 8 - N一样编码 + if num < 0: + num = 16 ** 8 + num + # 如果0的话那么会返回‘’ + if num == 0: + return '0' + char = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'] + ans = [] + while num > 0: + ans.append(char[num % 16]) + num = num // 16 + return ''.join(reversed(ans)) +``` + +```python +# 问题4: 消去x最后一位的1 +def remove_last_one(x): + return x & (x - 1) +# 获得最右边1开始至结尾 +def get_rightmost_one(x): + return x & (~ (x - 1)) +``` + + +```python +# 600 Non-negative Integers without Consecutive Ones +# 统计在[0, n]范围整数中,有多少个整数的二进制表示中不存在连续的1,0~2^i-1中没有连续1的个数是Fibonacci数列即可以看成10xxxx和0xxxxx +def findIntegers(n): + fib = [1, 2] + for i in range(2, 32): + fib.append(fib[-1] + fib[-2]) + res, k, pre = 0, 31, 0 + while k >= 0: + if n & (1 << k): + res += fib[k] + if pre: + return res # 连续出现两个1,停止 + pre = 1 + else: + pre = 0 + k -= 1 + return res + 1 # 说明 n 本身没有连续1,以上都是记录严格小于 n 的,所以要加上本身 +``` + + +```python +# 问题5: 判断是不是4的幂次 +def is_power_of_4(x): + chkbit = 3 + if (x & (x - 1)) != 0: + return False + while (chkbit & x) == 0: + x >>= 2 + return ((x & chkbit) == 1) +``` + +```python +# 问题6: Check有没有连续的1 +def has_consecutive_ones(n): + return n & (n >> 1) == 0 +``` + +```python +# 问题7: 定义加法,用 ^ 表示异或运算(XOR,Exclusive OR),两个输入值不相同时,输出为1;相同,输出为0。使用 & 表示按位与运算,而 << 表示左移操作。循环中的逻辑保持一致,直到 b 为 0 停止。这个自定义的加法实际上是直接对补码进行加法运算,因此对于负数同样成立。 +def custom_addition(a, b): + while b: + a_ = a ^ b + b_ = (a & b) << 1 + a, b = a_, b_ + return a +``` + + +```python +# 找出只出现一次的数 +# a ^ b ^ b = a +def find_single_occurrence(nums): + result = 0 + for num in nums: + result ^= num + return result +``` + +```python +# 找出两个只出现一次的数 +def find_two_single_occurrences(nums): + c = 0 + for num in nums: + c ^= num + k = c & (~(c - 1)) + a, b = 0, 0 + for num in nums: + if num & k == 0: + a ^= num + else: + b ^= num + return a, b +``` + + +```python +# 找出缺失的数, 0到2^n-1 少了一个数,全部异或起来,结果就是少的那个数。因为正常0,1都出现了偶数次,唯独少的那个数字对应的0/1出现奇数次。 +``` + +```python +#H 负二进制加法/Adding Two Negabinary Numbers +# 如果 x≥2,那么将 x 减去 2,并向高位进位 −1。即逢 2 进负 1。 +# 如果 x=−1,由于 −(−2)^i=(−2)^i +(−2)^{i+1},所以我们可以将 x 置为 1,并向高位进位 1。 +class Solution: + def addNegabinary(self, arr1: List[int], arr2: List[int]) -> List[int]: + i, j = len(arr1) - 1, len(arr2) - 1 + c = 0 + ans = [] + while i >= 0 or j >= 0 or c: + a = 0 if i < 0 else arr1[i] + b = 0 if j < 0 else arr2[j] + x = a + b + c + c = 0 + if x >= 2: + x -= 2 + c -= 1 + elif x == -1: + x = 1 + c += 1 + ans.append(x) + i, j = i - 1, j - 1 + while len(ans) > 1 and ans[-1] == 0: + ans.pop() + return ans[::-1] +``` + +```python +# 49.9 Pow(x, n) (Medium) +# 二进制解法 +class Solution: + def myPow(self, x: float, n: int) -> float: + ans = 1 + if n < 0: # x^-n = (1/x)^n + n = -n + x = 1 / x + while n: # 从低到高枚举 n 的每个比特位 + if n & 1: # 这个比特位是 1 + ans *= x # 把 x 乘到 ans 中 + x *= x # x 自身平方 + n >>= 1 # 继续枚举下一个比特位 + return ans +``` + + +# 贪心算法 + +```python +# 0 整数拆分 +class Solution: + def integerBreak(self, n): + if n == 2: # 当n等于2时,只有一种拆分方式:1+1=2,乘积为1 + return 1 + if n == 3: # 当n等于3时,只有一种拆分方式:1+1+1=3,乘积为1 + return 2 + if n == 4: # 当n等于4时,有两种拆分方式:2+2=4和1+1+1+1=4,乘积都为4 + return 4 + result = 1 + while n > 4: + result *= 3 # 每次乘以3,因为3的乘积比其他数字更大 + n -= 3 # 每次减去3 + result *= n # 将剩余的n乘以最后的结果 + return result +``` + +```python +# 455.分发饼干 +# 对每个孩子 i,都有一个胃口值 g[i],这是能让孩子们满足胃口的饼干的最小尺寸;并且每块饼干 j,都有一个尺寸 s[j] 。 +class Solution: + def findContentChildren(self, g, s): + g.sort() # 将孩子的贪心因子排序 + s.sort() # 将饼干的尺寸排序 + index = len(s) - 1 # 饼干数组的下标,从最后一个饼干开始 + result = 0 # 满足孩子的数量 + for i in range(len(g)-1, -1, -1): # 遍历胃口,从最后一个孩子开始 + if index >= 0 and s[index] >= g[i]: # 遍历饼干 + result += 1 + index -= 1 + return result +``` + +```python +# 55. 跳跃游戏 +# 数组元素代表你在该位置可以跳跃的最大长度。判断你是否能够到达最后一个位置。 +class Solution: + def canJump(self, nums: List[int]) -> bool: + cover = 0 + if len(nums) == 1: return True + i = 0 + # python不支持动态修改for循环中变量,使用while循环代替 + while i <= cover: + cover = max(i + nums[i], cover) + if cover >= len(nums) - 1: return True + i += 1 + return False +``` + + +```python +# 45.跳跃游戏 II +# 使用最少的跳跃次数到达数组的最后一个位置。 +class Solution: + def jump(self, nums): + if len(nums) == 1: + return 0 + cur_distance = 0 # 当前覆盖最远距离下标 + ans = 0 # 记录走的最大步数 + next_distance = 0 # 下一步覆盖最远距离下标 + for i in range(len(nums)): + next_distance = max(nums[i] + i, next_distance) # 更新下一步覆盖最远距离下标 + if i == cur_distance: # 遇到当前覆盖最远距离下标 + ans += 1 # 需要走下一步 + cur_distance = next_distance # 更新当前覆盖最远距离下标(相当于加油了) + if next_distance >= len(nums) - 1: # 当前覆盖最远距离达到数组末尾,不用再做ans++操作,直接结束 + break + return ans +``` + +```python +# 1005.K次取反后最大化的数组和 +# i 并将 A[i] 替换为 -A[i],然后总共重复这个过程 K 次。返回数组可能的最大和。 +class Solution: + def largestSumAfterKNegations(self, A: List[int], K: int) -> int: + A.sort(key=lambda x: abs(x), reverse=True) # 第一步:按照绝对值降序排序数组A + for i in range(len(A)): # 第二步:执行K次取反操作 + if A[i] < 0 and K > 0: + A[i] *= -1 + K -= 1 + if K % 2 == 1: # 第三步:如果K还有剩余次数,将绝对值最小的元素取反 + A[-1] *= -1 + result = sum(A) # 第四步:计算数组A的元素和 + return result +``` + + +```python +# 134 加油站 +# 环路N个加油站,第i个加油站有汽油 gas[i] 升。你有一辆油箱容量无限的的汽车,从第 i个加油站开往第i+1 个加油站需要消耗汽油cost[i]升。你从其中的一个加油站出发,开始时油箱为空。如果你可以绕环路行驶一周,则返回出发时加油站的编号,否则返回 -1。首先检查第 0 个加油站,并试图判断能否环绕一周;如果不能,就从第一个无法到达的加油站开始继续检查。因为,如果x到达不了y+1,那么x-y之间的点也不可能到达y+1,因为中间任何一点的油都是拥有前面的余量的,所以下次遍历直接从y+1 开始。 +class Solution: + def canCompleteCircuit(self, gas: List[int], cost: List[int]) -> int: + n = len(gas) + i = 0 + while i < n: + sum_of_gas = sum_of_cost = 0 + cnt = 0 + while cnt < n: + j = (i + cnt) % n + sum_of_gas += gas[j] + sum_of_cost += cost[j] + if sum_of_cost > sum_of_gas: + break + cnt += 1 + if cnt == n: + return i + else: + i += cnt + 1 + return -1 +``` + + +```python +# 135. 分发糖果 +# 每个孩子至少分配到 1 个糖果。相邻的孩子中,评分高的孩子必须获得更多的糖果。老师至少需要准备多少颗糖果呢 +class Solution: + def candy(self, ratings: List[int]) -> int: + candyVec = [1] * len(ratings) + # 从前向后遍历,处理右侧比左侧评分高的情况 + for i in range(1, len(ratings)): + if ratings[i] > ratings[i - 1]: + candyVec[i] = candyVec[i - 1] + 1 + # 从后向前遍历,处理左侧比右侧评分高的情况 + for i in range(len(ratings) - 2, -1, -1): + if ratings[i] > ratings[i + 1]: + candyVec[i] = max(candyVec[i], candyVec[i + 1] + 1) + # 统计结果 + result = sum(candyVec) + return result +``` + +```python +# 860.柠檬水找零 +# 向你付 5 美元、10 美元或 20 美元。你必须给每个顾客正确找零 +class Solution: + def lemonadeChange(self, bills: List[int]) -> bool: + five = 0 + ten = 0 + twenty = 0 + for bill in bills: + if bill == 5: + five += 1 + if bill == 10: + if five <= 0: + return False + ten += 1 + five -= 1 + if bill == 20: + # 先尝试使用10美元和5美元找零 + if five > 0 and ten > 0: + five -= 1 + ten -= 1 + #twenty += 1 + # 如果无法使用10美元找零,则尝试使用三张5美元找零 + elif five >= 3: + five -= 3 + #twenty += 1 + else: + return False + return True +``` + +```python +# 452. 用最少数量的箭引爆气球 +class Solution: + def findMinArrowShots(self, points: List[List[int]]) -> int: + if len(points) == 0: return 0 + points.sort(key=lambda x: x[0]) + result = 1 + for i in range(1, len(points)): + if points[i][0] > points[i - 1][1]: # 气球i和气球i-1不挨着,注意这里不是>= + result += 1 + else: + points[i][1] = min(points[i - 1][1], points[i][1]) # 更新重叠气球最小右边界 + return result +``` + + +```python +# 0 无重叠区间 +# 找到需要移除区间的最小数量,使剩余区间互不重叠。 +class Solution: + def eraseOverlapIntervals(self, intervals: List[List[int]]) -> int: + if not intervals: + return 0 + intervals.sort(key=lambda x: x[0]) # 按照左边界升序排序 + count = 0 # 记录重叠区间数量 + for i in range(1, len(intervals)): + if intervals[i][0] < intervals[i - 1][1]: # 存在重叠区间 + intervals[i][1] = min(intervals[i - 1][1], intervals[i][1]) # 更新重叠区间的右边界 + count += 1 + return count +``` + +```python +# 0 合并区间 +class Solution: + def merge(self, intervals): + result = [] + if len(intervals) == 0: + return result # 区间集合为空直接返回 + intervals.sort(key=lambda x: x[0]) # 按照区间的左边界进行排序 + result.append(intervals[0]) # 第一个区间可以直接放入结果集中 + for i in range(1, len(intervals)): + if result[-1][1] >= intervals[i][0]: # 发现重叠区间 + # 合并区间,只需要更新结果集最后一个区间的右边界,因为根据排序,左边界已经是最小的 + result[-1][1] = max(result[-1][1], intervals[i][1]) + else: + result.append(intervals[i]) # 区间不重叠 + return result +``` +```python +# 0 单调递增的数字 +# 找出小于或等于 N 的最大的整数,同时这个整数需要满足其各个位数上的数字是单调递增。 +class Solution: + def monotoneIncreasingDigits(self, N: int) -> int: + # 将整数转换为字符串 + strNum = str(N) + # flag用来标记赋值9从哪里开始 + # 设置为字符串长度,为了防止第二个for循环在flag没有被赋值的情况下执行 + flag = len(strNum) + # 从右往左遍历字符串 + for i in range(len(strNum) - 1, 0, -1): + # 如果当前字符比前一个字符小,说明需要修改前一个字符 + if strNum[i - 1] > strNum[i]: + flag = i # 更新flag的值,记录需要修改的位置 + # 将前一个字符减1,以保证递增性质 + strNum = strNum[:i - 1] + str(int(strNum[i - 1]) - 1) + strNum[i:] + # 将flag位置及之后的字符都修改为9,以保证最大的递增数字 + for i in range(flag, len(strNum)): + strNum = strNum[:i] + '9' + strNum[i + 1:] + # 将最终的字符串转换回整数并返回 + return int(strNum) +``` + +```python +# 763.划分字母区间 +# 划分为尽可能多的片段,同一字母最多出现在一个片段中。 +class Solution: + def partitionLabels(self, s: str) -> List[int]: + last_occurrence = {} # 存储每个字符最后出现的位置 + for i, ch in enumerate(s): + last_occurrence[ch] = i + result = [] + start = 0 + end = 0 + for i, ch in enumerate(s): + end = max(end, last_occurrence[ch]) # 找到当前字符出现的最远位置 + if i == end: # 如果当前位置是最远位置,表示可以分割出一个区间 + result.append(end - start + 1) + start = i + 1 + return result +``` + + +```python +# 1029 n个长为2数组一半取第一个i[0],一半取第二个i[1],求和最小。贪心 +def twoCitySchedCost(costs): + # 按照费用差排序 + costs.sort(key=lambda x: x[0] - x[1]) + n = len(costs) // 2 + total_cost = 0 + # 前半部分去A城市,后半部分去B城市 + for i in range(n): + total_cost += costs[i][0] + for i in range(n, len(costs)): + total_cost += costs[i][1] + return total_cost +``` + +```python +# 628.给定一个整数数组,找到由数组中的三个整数组成的最大乘积,并输出这个乘积。贪心 +def maximumProduct(nums): + nums.sort() + n = len(nums) + # 情况1:三个数都是正数,直接取数组最后三个数相乘 + max_prod1 = nums[n - 1] * nums[n - 2] * nums[n - 3] + # 情况2:有一个或两个数是负数,取数组中最小的两个负数和最大的正数相乘 + max_prod2 = nums[0] * nums[1] * nums[n - 1] + # 返回两种情况中的较大值 + return max(max_prod1, max_prod2) +``` + +```python +# 179 Largest Number/最大数 (Medium) +# 定义序,证明是一个序 +class Solution: + def largestNumber(self, nums: List[int]) -> str: + def sort_rule(x, y): + a, b = x + y, y + x + if a < b: return 1 + elif a > b: return -1 + else: return 0 + + strs = [str(num) for num in nums] + strs.sort(key = cmp_to_key(sort_rule)) + if strs[0] == "0": + return "0" + return ''.join(strs) +``` +```python +# 563.9 Find the Closest Palindrome/寻找最近的回文数 (Hard) +# 先把前一半拿出来,再倒过来给后一半补全 +class Solution: + def nearestPalindromic(self, n: str) -> str: + length, int_n = len(n), int(n) + if int_n < 10 or int_n == 10 ** (length-1): return str(int_n-1) + if int_n - 1 == 10 ** (length-1): return str(int_n-2) + if int_n + 1 == 10 ** length: return str(int_n + 2) + pre = int(n[:(length+1)//2]) + min_diff = float('inf') + for dx in (-1, 0, 1): + s = str(pre + dx) + candidate = s[:length//2] + s[::-1] + if candidate != n: + diff = abs(int(candidate) - int_n) + if diff < min_diff: + min_diff = diff + result = candidate + return result +``` + + +# 树Tree +```python +#R 二叉树的迭代遍历 +# 前序遍历-迭代-LC144_二叉树的前序遍历 +class Solution: + def preorderTraversal(self, root: TreeNode) -> List[int]: + # 根结点为空则返回空列表 + if not root: + return [] + stack = [root] + result = [] + while stack: + node = stack.pop() + # 中结点先处理 + result.append(node.val) + # 右孩子先入栈 + if node.right: + stack.append(node.right) + # 左孩子后入栈 + if node.left: + stack.append(node.left) + return result +``` + +```python +#R 中序遍历-迭代-LC94_二叉树的中序遍历 +class Solution: + def inorderTraversal(self, root: TreeNode) -> List[int]: + if not root: + return [] + stack = [] # 不能提前将root结点加入stack中 + result = [] + cur = root + while cur or stack: + # 先迭代访问最底层的左子树结点 + if cur: + stack.append(cur) + cur = cur.left + # 到达最左结点后处理栈顶结点 + else: + cur = stack.pop() + result.append(cur.val) + # 取栈顶元素右结点 + cur = cur.right + return result +``` + + +```python +# 后序遍历-迭代-LC145_二叉树的后序遍历 +class Solution: + def postorderTraversal(self, root: TreeNode) -> List[int]: + if not root: + return [] + stack = [root] + result = [] + while stack: + node = stack.pop() + # 中结点先处理 + result.append(node.val) + # 左孩子先入栈 + if node.left: + stack.append(node.left) + # 右孩子后入栈 + if node.right: + stack.append(node.right) + # 将最终的数组翻转 + return result[::-1] +``` + +```python +#R 102.二叉树的层序遍历 +# 利用长度法 +# Definition for a binary tree node. +# class TreeNode: +# def __init__(self, val=0, left=None, right=None): +# self.val = val +# self.left = left +# self.right = right +class Solution: + def levelOrder(self, root: Optional[TreeNode]) -> List[List[int]]: + if not root: + return [] + queue = collections.deque([root]) + result = [] + while queue: + level = [] + for _ in range(len(queue)): + cur = queue.popleft() + level.append(cur.val) + if cur.left: + queue.append(cur.left) + if cur.right: + queue.append(cur.right) + result.append(level) + return result +``` + + +```python +# 226.翻转二叉树 +class Solution: + def invertTree(self, root: TreeNode) -> TreeNode: + if not root: + return None + root.left, root.right = root.right, root.left + self.invertTree(root.left) + self.invertTree(root.right) + return root +``` + + +```python +# 101. 对称二叉树 +class Solution: + def isSymmetric(self, root: TreeNode) -> bool: + if not root: + return True + return self.compare(root.left, root.right) + def compare(self, left, right): + #首先排除空节点的情况 + if left == None and right != None: return False + elif left != None and right == None: return False + elif left == None and right == None: return True + #排除了空节点,再排除数值不相同的情况 + elif left.val != right.val: return False + #此时就是:左右节点都不为空,且数值相同的情况 + #此时才做递归,做下一层的判断 + outside = self.compare(left.left, right.right) #左子树:左、 右子树:右 + inside = self.compare(left.right, right.left) #左子树:右、 右子树:左 + isSame = outside and inside #左子树:中、 右子树:中 (逻辑处理) + return isSame +``` + +```python +# 104.二叉树的最大深度 +class solution: + def maxdepth(self, root: treenode) -> int: + return self.getdepth(root) + def getdepth(self, node): + if not node: + return 0 + leftheight = self.getdepth(node.left) #左 + rightheight = self.getdepth(node.right) #右 + height = 1 + max(leftheight, rightheight) #中 + return height +``` + +```python +# 111.二叉树的最小深度 +class Solution: + def getDepth(self, node): + if node is None: + return 0 + leftDepth = self.getDepth(node.left) # 左 + rightDepth = self.getDepth(node.right) # 右 + # 当一个左子树为空,右不为空,这时并不是最低点 + if node.left is None and node.right is not None: + return 1 + rightDepth + # 当一个右子树为空,左不为空,这时并不是最低点 + if node.left is not None and node.right is None: + return 1 + leftDepth + result = 1 + min(leftDepth, rightDepth) + return result + def minDepth(self, root): + return self.getDepth(root) +``` + + +```python +# 222.完全二叉树的节点个数 +class Solution: + def countNodes(self, root: TreeNode) -> int: + return self.getNodesNum(root) + def getNodesNum(self, cur): + if not cur: + return 0 + leftNum = self.getNodesNum(cur.left) #左 + rightNum = self.getNodesNum(cur.right) #右 + treeNum = leftNum + rightNum + 1 #中 + return treeNum +``` + +```python +# 110.平衡二叉树 +# 求深度适合用前序遍历,而求高度适合用后序遍历 +class Solution: + def isBalanced(self, root: TreeNode) -> bool: + if self.get_height(root) != -1: + return True + else: + return False + def get_height(self, root: TreeNode) -> int: + # Base Case + if not root: + return 0 + if (left_height := self.get_height(root.left)) == -1: + return -1 + if (right_height := self.get_height(root.right)) == -1: + return -1 + if abs(left_height - right_height) > 1: + return -1 + else: + return 1 + max(left_height, right_height) +``` + +```python +112. 路径总和 +class Solution: + def traversal(self, cur: TreeNode, count: int) -> bool: + if not cur.left and not cur.right and count == 0: # 遇到叶子节点,并且计数为0 + return True + if not cur.left and not cur.right: # 遇到叶子节点直接返回 + return False + if cur.left: # 左 + count -= cur.left.val + if self.traversal(cur.left, count): # 递归,处理节点 + return True + count += cur.left.val # 回溯,撤销处理结果 + if cur.right: # 右 + count -= cur.right.val + if self.traversal(cur.right, count): # 递归,处理节点 + return True + count += cur.right.val # 回溯,撤销处理结果 + return False + def hasPathSum(self, root: TreeNode, sum: int) -> bool: + if root is None: + return False + return self.traversal(root, sum - root.val) +``` + +```python +# 257. 二叉树的所有路径 +class Solution: + def traversal(self, cur, path, result): + path.append(cur.val) # 中 + if not cur.left and not cur.right: # 到达叶子节点 + sPath = '->'.join(map(str, path)) + result.append(sPath) + return + if cur.left: # 左 + self.traversal(cur.left, path, result) + path.pop() # 回溯 + if cur.right: # 右 + self.traversal(cur.right, path, result) + path.pop() # 回溯 + def binaryTreePaths(self, root): + result = [] + path = [] + if not root: + return result + self.traversal(root, path, result) + return result +``` + +```python +# 404.左叶子之和 +class Solution: + def sumOfLeftLeaves(self, root): + if root is None: + return 0 + if root.left is None and root.right is None: + return 0 + leftValue = self.sumOfLeftLeaves(root.left) # 左 + if root.left and not root.left.left and not root.left.right: # 左子树是左叶子的情况 + leftValue = root.left.val + rightValue = self.sumOfLeftLeaves(root.right) # 右 + sum_val = leftValue + rightValue # 中 + return sum_val +``` + +```python +# 513.找树左下角的值 +from collections import deque +class Solution: + def findBottomLeftValue(self, root): + if root is None: + return 0 + queue = deque() + queue.append(root) + result = 0 + while queue: + size = len(queue) + for i in range(size): + node = queue.popleft() + if i == 0: + result = node.val + if node.left: + queue.append(node.left) + if node.right: + queue.append(node.right) + return result +``` + + + +```python +105.从前序与中序遍历序列构造二叉树 +class Solution: + def buildTree(self, preorder: List[int], inorder: List[int]) -> TreeNode: + # 第一步: 特殊情况讨论: 树为空. 或者说是递归终止条件 + if not preorder: + return None + # 第二步: 前序遍历的第一个就是当前的中间节点. + root_val = preorder[0] + root = TreeNode(root_val) + # 第三步: 找切割点. + separator_idx = inorder.index(root_val) + # 第四步: 切割inorder数组. 得到inorder数组的左,右半边. + inorder_left = inorder[:separator_idx] + inorder_right = inorder[separator_idx + 1:] + # 第五步: 切割preorder数组. 得到preorder数组的左,右半边. + # ⭐️ 重点1: 中序数组大小一定跟前序数组大小是相同的. + preorder_left = preorder[1:1 + len(inorder_left)] + preorder_right = preorder[1 + len(inorder_left):] + # 第六步: 递归 + root.left = self.buildTree(preorder_left, inorder_left) + root.right = self.buildTree(preorder_right, inorder_right) + # 第七步: 返回答案 + return root +``` + +```python +106.从中序与后序遍历序列构造二叉树 +class Solution: + def buildTree(self, inorder: List[int], postorder: List[int]) -> TreeNode: + if not postorder: + return None + root_val = postorder[-1] + root = TreeNode(root_val) + separator_idx = inorder.index(root_val) + inorder_left = inorder[:separator_idx] + inorder_right = inorder[separator_idx + 1:] + postorder_left = postorder[:len(inorder_left)] + postorder_right = postorder[len(inorder_left): len(postorder) - 1] + root.left = self.buildTree(inorder_left, postorder_left) + root.right = self.buildTree(inorder_right, postorder_right) + return root +``` + +```python +# 0 构建最大二叉树 +# 二叉树的根是数组中的最大元素。左子树是通过数组中最大值左边部分构造出的最大二叉树。右子树是通过最大值右边部分构造。 +class Solution: + def constructMaximumBinaryTree(self, nums: List[int]) -> TreeNode: + if len(nums) == 1: + return TreeNode(nums[0]) + node = TreeNode(0) + # 找到数组中最大的值和对应的下标 + maxValue = 0 + maxValueIndex = 0 + for i in range(len(nums)): + if nums[i] > maxValue: + maxValue = nums[i] + maxValueIndex = i + node.val = maxValue + # 最大值所在的下标左区间 构造左子树 + if maxValueIndex > 0: + new_list = nums[:maxValueIndex] + node.left = self.constructMaximumBinaryTree(new_list) + # 最大值所在的下标右区间 构造右子树 + if maxValueIndex < len(nums) - 1: + new_list = nums[maxValueIndex+1:] + node.right = self.constructMaximumBinaryTree(new_list) + return node +``` + +```python +# 0 合并二叉树 +class Solution: + def mergeTrees(self, root1: TreeNode, root2: TreeNode) -> TreeNode: + # 递归终止条件: + # 但凡有一个节点为空, 就立刻返回另外一个. 如果另外一个也为None就直接返回None. + if not root1: + return root2 + if not root2: + return root1 + # 上面的递归终止条件保证了代码执行到这里root1, root2都非空. + root1.val += root2.val # 中 + root1.left = self.mergeTrees(root1.left, root2.left) #左 + root1.right = self.mergeTrees(root1.right, root2.right) # 右 + return root1 # 注意: 本题我们重复使用了题目给出的节点而不是创建新节点. 节省时间, 空间. +``` + +```python +# 700.二叉搜索树中的搜索 +class Solution: + def searchBST(self, root: TreeNode, val: int) -> TreeNode: + # 为什么要有返回值: + # 因为搜索到目标节点就要立即return, + # 这样才是找到节点就返回(搜索某一条边),如果不加return,就是遍历整棵树了。 + if not root or root.val == val: + return root + if root.val > val: + return self.searchBST(root.left, val) + if root.val < val: + return self.searchBST(root.right, val) +``` + + +```python +# 0 验证二叉搜索树 +class Solution: + def __init__(self): + self.vec = [] + def traversal(self, root): + if root is None: + return + self.traversal(root.left) + self.vec.append(root.val) # 将二叉搜索树转换为有序数组 + self.traversal(root.right) + def isValidBST(self, root): + self.vec = [] # 清空数组 + self.traversal(root) + for i in range(1, len(self.vec)): + # 注意要小于等于,搜索树里不能有相同元素 + if self.vec[i] <= self.vec[i - 1]: + return False + return True +``` + +```python +# 0 二叉搜索树的最小绝对差 +非负值的二叉搜索树,请你计算树中任意两节点的差的绝对值的最小值。 +class Solution: + def __init__(self): + self.result = float('inf') + self.pre = None + def traversal(self, cur): + if cur is None: + return + self.traversal(cur.left) # 左 + if self.pre is not None: # 中 + self.result = min(self.result, cur.val - self.pre.val) + self.pre = cur # 记录前一个 + self.traversal(cur.right) # 右 + def getMinimumDifference(self, root): + self.traversal(root) + return self.result +``` + + +```python +# 501.二叉搜索树中的众数 +class Solution: + def __init__(self): + self.maxCount = 0 # 最大频率 + self.count = 0 # 统计频率 + self.pre = None + self.result = [] + def searchBST(self, cur): + if cur is None: + return + self.searchBST(cur.left) # 左 + # 中 + if self.pre is None: # 第一个节点 + self.count = 1 + elif self.pre.val == cur.val: # 与前一个节点数值相同 + self.count += 1 + else: # 与前一个节点数值不同 + self.count = 1 + self.pre = cur # 更新上一个节点 + if self.count == self.maxCount: # 如果与最大值频率相同,放进result中 + self.result.append(cur.val) + if self.count > self.maxCount: # 如果计数大于最大值频率 + self.maxCount = self.count # 更新最大频率 + self.result = [cur.val] # 很关键的一步,不要忘记清空result,之前result里的元素都失效了 + self.searchBST(cur.right) # 右 + return + def findMode(self, root): + self.count = 0 + self.maxCount = 0 + self.pre = None # 记录前一个节点 + self.result = [] + self.searchBST(root) + return self.result +``` + +```python +# 0 把二叉搜索树转换为累加树 +# 每个节点 node 的新值等于原树中大于或等于 node.val 的值之和 +class Solution: + def convertBST(self, root: TreeNode) -> TreeNode: + self.pre = 0 # 记录前一个节点的数值 + self.traversal(root) + return root + def traversal(self, cur): + if cur is None: + return + self.traversal(cur.right) + cur.val += self.pre + self.pre = cur.val + self.traversal(cur.left) +``` + +```python +# 236. 二叉树的最近公共祖先 +class Solution: + def lowestCommonAncestor(self, root, p, q): + if root == q or root == p or root is None: + return root + left = self.lowestCommonAncestor(root.left, p, q) + right = self.lowestCommonAncestor(root.right, p, q) + if left is not None and right is not None: + return root + if left is None and right is not None: + return right + elif left is not None and right is None: + return left + else: + return None +``` + +```python +# 235. 二叉搜索树的最近公共祖先 +class Solution: + def lowestCommonAncestor(self, root, p, q): + if root.val < p.val and root.val < q.val: + return self.lowestCommonAncestor(root.right, p, q) + if root.val > p.val and root.val > q.val: + return self.lowestCommonAncestor(root.left, p, q) + return root +``` + +```python +# 0 二叉搜索树中的插入操作 +class Solution: + def insertIntoBST(self, root, val): + if root is None: + node = TreeNode(val) + return node + if root.val > val: + root.left = self.insertIntoBST(root.left, val) + if root.val < val: + root.right = self.insertIntoBST(root.right, val) + return root +``` + + +```python +# 0 删除二叉搜索树中的节点 +# 第一种情况:没找到删除的节点,遍历到空节点直接返回了 +# 找到删除的节点 +# 第二种情况:左右孩子都为空(叶子节点),直接删除节点, 返回NULL为根节点 +# 第三种情况:删除节点的左孩子为空,右孩子不为空,删除节点,右孩子补位,返回右孩子为根节点 +# 第四种情况:删除节点的右孩子为空,左孩子不为空,删除节点,左孩子补位,返回左孩子为根节点 +# 第五种情况:左右孩子节点都不为空,则将删除节点的左子树头结点(左孩子)放到删除节点的右子树的最左面节点的左孩子上,返回删除节点右孩子为新的根节点。 +class Solution: + def deleteNode(self, root, key): + if root is None: + return root + if root.val == key: + if root.left is None and root.right is None: + return None + elif root.left is None: + return root.right + elif root.right is None: + return root.left + else: + cur = root.right + while cur.left is not None: + cur = cur.left + cur.left = root.left + return root.right + if root.val > key: + root.left = self.deleteNode(root.left, key) + if root.val < key: + root.right = self.deleteNode(root.right, key) + return root +``` + + +```python +669. 修剪二叉搜索树 +通过修剪二叉搜索树,使得所有节点的值在[L, R] +class Solution: + def trimBST(self, root: TreeNode, low: int, high: int) -> TreeNode: + if root is None: + return None + if root.val < low: + # 寻找符合区间 [low, high] 的节点 + return self.trimBST(root.right, low, high) + if root.val > high: + # 寻找符合区间 [low, high] 的节点 + return self.trimBST(root.left, low, high) + root.left = self.trimBST(root.left, low, high) # root.left 接入符合条件的左孩子 + root.right = self.trimBST(root.right, low, high) # root.right 接入符合条件的右孩子 + return root +``` + +```python +# 108.将有序数组转换为二叉搜索树 +class Solution: + def traversal(self, nums: List[int], left: int, right: int) -> TreeNode: + if left > right: + return None + mid = left + (right - left) // 2 + root = TreeNode(nums[mid]) + root.left = self.traversal(nums, left, mid - 1) + root.right = self.traversal(nums, mid + 1, right) + return root + def sortedArrayToBST(self, nums: List[int]) -> TreeNode: + root = self.traversal(nums, 0, len(nums) - 1) + return root +``` + +```python +#R 671. 二叉树中第二小的节点。 +# 给定一个非空特殊的二叉树,每个节点都是正数,并且每个节点的子节点数量只能为 2 或 0。如果一个节点有两个子节点的话,那么该节点的值等于两个子节点中较小的一个。 +class Solution: + def findSecondMinimumValue(self, root: TreeNode) -> int: + if not root or not root.left: + return -1 + # 我们知道root.val是最小值,那么 + # 第二小的值存在于 更小的子节点那一边的子树的第二小的值 或 更大的子节点 之中 + left = root.left.val if root.left.val != root.val else self.findSecondMinimumValue(root.left) + right = root.right.val if root.right.val != root.val else self.findSecondMinimumValue(root.right) + return min(left, right) if left != -1 and right != -1 else max(left, right) +``` + + +```python +662. Maximum Width of Binary Tree +from collections import deque +class TreeNode: + def __init__(self, val=0, left=None, right=None): + self.val = val + self.left = left + self.right = right +def widthOfBinaryTree(root): + if not root: + return 0 + queue = deque([(root, 1)]) + res = 0 + while queue: + n = len(queue) + if n == 1: + queue[0] = (queue[0][0], 1) # 如果这一层只有一个节点,将左索引重置为1 + l, r = queue[0][1], queue[-1][1] + for i in range(n): + top, idx = queue.popleft() + if top.left: + queue.append((top.left, idx * 2)) + if top.right: + queue.append((top.right, idx * 2 + 1)) + res = max(res, r - l + 1) + return res +``` + +```python +#R 208 前缀树是一种特殊的多叉树,它的 TrieNode 中 chidren 是一个大小为 26 的一维数组,分别对应了26个英文字符 void insert(String word) 向前缀树中插入字符串 word,search(String word) 如果字符串 word 在前缀树中,返回 true(即,在检索之前已经插入);否则,返回 false 。 startsWith 如果之前已经插入的字符串 word 的前缀之一为 prefix ,返回 true ;否则,返回 false 。 +class Trie: + def __init__(self): + self.children = [None] * 26 + self.isEnd = False + def searchPrefix(self, prefix: str) -> "Trie": + node = self + for ch in prefix: + ch = ord(ch) - ord("a") + if not node.children[ch]: + return None + node = node.children[ch] + return node + def insert(self, word: str) -> None: + node = self + for ch in word: + ch = ord(ch) - ord("a") + if not node.children[ch]: + node.children[ch] = Trie() + node = node.children[ch] + node.isEnd = True + def search(self, word: str) -> bool: + node = self.searchPrefix(word) + return node is not None and node.isEnd + def startsWith(self, prefix: str) -> bool: + return self.searchPrefix(prefix) is not None +``` + +```python +#R 1032. Stream of Characters 接收一个字符流,并检查这些字符的后缀是否是字符串数组 words 中的一个字符串。 +class Trie: + def __init__(self): + self.children = [None] * 26 + self.is_end = False + def insert(self, w: str): + node = self + for c in w[::-1]: + idx = ord(c) - ord('a') + if node.children[idx] is None: + node.children[idx] = Trie() + node = node.children[idx] + node.is_end = True + def search(self, w: List[str]) -> bool: + node = self + for c in w[::-1]: + idx = ord(c) - ord('a') + if node.children[idx] is None: + return False + node = node.children[idx] + if node.is_end: + return True + return False +class StreamChecker: + def __init__(self, words: List[str]): + self.trie = Trie() + self.cs = [] + self.limit = 201 + for w in words: + self.trie.insert(w) + def query(self, letter: str) -> bool: + self.cs.append(letter) + return self.trie.search(self.cs[-self.limit:]) +# Your StreamChecker object will be instantiated and called as such: +# obj = StreamChecker(words) +# param_1 = obj.query(letter) +``` + + + + + +```python +# 95 Unique Binary Search Trees II/不同的二叉搜索树 II (Medium) +class Solution: + def generateTrees(self, n: int) -> List[Optional[TreeNode]]: + def dfs(l, r): + if l > r: + return [None] + ans = [] + for i in range(l, r + 1): + for x in dfs(l, i - 1): + for y in dfs(i + 1, r): + root = TreeNode(i) + root.left, root.right = x, y + ans.append(root) + return ans + return dfs(1, n) +``` +```python +# 100 Same Tree/相同的树 (Easy) +class Solution: + def isSameTree(self, p: Optional[TreeNode], q: Optional[TreeNode]) -> bool: + if p is None or q is None: + return p is q # 必须都是 None + return p.val == q.val and self.isSameTree(p.left, q.left) and self.isSameTree(p.right, q.right) +``` +```python +# 109 Convert Sorted List to Binary Search Tree/有序链表转换二叉搜索树 (Medium) +class Solution: + def sortedListToBST(self, head: ListNode) -> TreeNode: + def findmid(head, tail): + slow = head + fast = head + while fast != tail and fast.next!= tail : + slow = slow.next + fast = fast.next.next + return slow + def helper(head, tail): + if head == tail: return + node = findmid(head, tail) + root = TreeNode(node.val) + root.left = helper(head, node) + root.right = helper(node.next, tail) + return root + return helper(head, None) +``` +```python +# 112.99 Path Sum II +class Solution: + def pathSum(self, root: Optional[TreeNode], targetSum: int) -> List[List[int]]: + res, path = [], [] + def recur(root, tar): + if not root: return + path.append(root.val) + tar -= root.val + if tar == 0 and not root.left and not root.right: + res.append(list(path)) + recur(root.left, tar) + recur(root.right, tar) + path.pop() + recur(root, targetSum) + return res +``` +```python +#R 124 Binary Tree Maximum Path Sum/二叉树中的最大路径和 (Hard) +# 输入:root = [1,2,3], 最优路径是 2 -> 1 -> 3 +# 链:从下面的某个节点(不一定是叶子)到当前节点的路径。把这条链的节点值之和,作为 dfs 的返回值。如果节点值之和是负数,则返回 0。 +# 直径:等价于由两条(或者一条)链拼成的路径。我们枚举每个 node,假设直径在这里「拐弯」,也就是计算由左右两条从下面的某个节点(不一定是叶子)到 node 的链的节点值之和,去更新答案的最大值。 +# dfs 返回的是链的节点值之和,不是直径的节点值之和。 +class Solution: + def maxPathSum(self, root: Optional[TreeNode]) -> int: + ans = -inf + def dfs(node: Optional[TreeNode]) -> int: + if node is None: + return 0 # 没有节点,和为 0 + l_val = dfs(node.left) # 左子树最大链和 + r_val = dfs(node.right) # 右子树最大链和 + nonlocal ans + ans = max(ans, l_val + r_val + node.val) # 两条链拼成路径 + return max(max(l_val, r_val) + node.val, 0) # 当前子树最大链和(注意这里和 0 取最大值了) + dfs(root) + return ans +``` +```python +#R 297 Serialize and Deserialize Binary Tree/二叉树的序列化与反序列化 (Medium) +# 序列化 使用层序遍历实现。反序列化通过递推公式反推各节点在序列中的索引 +class Codec: + def serialize(self, root): + if not root: return "[]" + queue = collections.deque() + queue.append(root) + res = [] + while queue: + node = queue.popleft() + if node: + res.append(str(node.val)) + queue.append(node.left) + queue.append(node.right) + else: res.append("null") + return '[' + ','.join(res) + ']' + + def deserialize(self, data): + if data == "[]": return + vals, i = data[1:-1].split(','), 1 + root = TreeNode(int(vals[0])) + queue = collections.deque() + queue.append(root) + while queue: + node = queue.popleft() + if vals[i] != "null": + node.left = TreeNode(int(vals[i])) + queue.append(node.left) + i += 1 + if vals[i] != "null": + node.right = TreeNode(int(vals[i])) + queue.append(node.right) + i += 1 + return root +``` +```python +#R 366 Find Leaves of Binary Tree $/寻找二叉树的叶子节点 (Medium) +# 收集所有的叶子节点;移除所有的叶子节点;重复以上步骤,直到树为空。 高度代表倒数第几个叶子节点 +class Solution: + def findLeaves(self, root: TreeNode) -> List[List[int]]: + # 自底向上递归 + def dfs(root): + if not root:return 0 + l,r=dfs(root.left),dfs(root.right) + depth=max(l,r)+1 + res[depth].append(root.val) + return depth + + res=collections.defaultdict(list) + dfs(root) + return [v for v in res.values()] +``` +```python +# 606 Construct String from Binary Tree/根据二叉树创建字符串 (中等) +# 输入:root = [1,2,3,null,4] 输出:"1(2()(4))(3)" +class Solution: + def tree2str(self, root: Optional[TreeNode]) -> str: + if root is None: + return "" + if root.left is None and root.right is None: + return str(root.val) + if root.right is None: + return f"{root.val}({self.tree2str(root.left)})" + return f"{root.val}({self.tree2str(root.left)})({self.tree2str(root.right)})" +``` +```python +#R 652 Find Duplicate Subtrees/寻找重复的子树 (Medium) +# 递归的方法进行序列化 +class Solution: + def findDuplicateSubtrees(self, root: Optional[TreeNode]) -> List[Optional[TreeNode]]: + def dfs(node: Optional[TreeNode]) -> str: + if not node: + return "" + serial = "".join([str(node.val), "(", dfs(node.left), ")(", dfs(node.right), ")"]) + if (tree := seen.get(serial, None)): + repeat.add(tree) + else: + seen[serial] = node + return serial + seen = dict() + repeat = set() + dfs(root) + return list(repeat) +``` +```python +# 663 Equal Tree Partition $/均匀树划分 (Medium) +# 是否可以通过去掉原始树上的一条边将树分成两棵节点值之和相等的子树 +# 记录下每个子树的和。我们可以使用深度优先搜索递归地完成此操作。然后,我们应检查整棵树的一半的和是否出现在我们的记录中 +class Solution(object): + def checkEqualTree(self, root): + seen = [] + def sum_(node): + if not node: return 0 + seen.append(sum_(node.left) + sum_(node.right) + node.val) + return seen[-1] + + total = sum_(root) + seen.pop() + return total / 2.0 in seen +``` +```python +#R 0 My Calendar II/我的日程安排表 II (Medium) +# 如果要添加的时间内不会导致三重预订时,则可以存储这个新的日程安排。 +class MyCalendarTwo: + def __init__(self): + self.booked = [] + self.overlaps = [] + def book(self, start: int, end: int) -> bool: + if any(s < end and start < e for s, e in self.overlaps): + return False + # 注意两个区间不相交的补=s < end and start < e + for s, e in self.booked: + if s < end and start < e: + self.overlaps.append((max(s, start), min(e, end))) + self.booked.append((start, end)) + return True +``` + +```python +#R 987 Vertical Order Traversal of a Binary Tree/二叉树的垂序遍历 (Medium) +class Solution: + def verticalTraversal(self, root: Optional[TreeNode]) -> List[List[int]]: + groups = defaultdict(list) + def dfs(node, row, col): #二叉树先序遍历 + if node is None: + return + groups[col].append((row, node.val)) # col 相同的分到同一组 + dfs(node.left, row + 1, col - 1) + dfs(node.right, row + 1, col + 1) + + dfs(root, 0, 0) + ans = [] + for _, g in sorted(groups.items()): + g.sort() # 按照 row 排序,row 相同按照 val 排序 + ans.append([val for _, val in g]) + return ans +```