LeetCode 71-90题
正文
本博客记录的是 LeetCode 71 到 90 题的题解
# 71. Simplify Path class Solution: def simplifyPath(self, path: str) -> str: new_dir = [] for name in path.split('/'): if name not in ['.', '..', '']: new_dir.append(name) if name == '..': if new_dir: new_dir.pop() return '/' + '/'.join(new_dir) # 72. Edit Distance a += ' ' a = ' ' + a f[i][j] = min(f[i - 1][j - 1], f[i - 1][j], f[i][j - 1]) + 1 # 78. Subsets n, res = len(nums), [] for i in range(1 << n): tmp_res = [] for j in range(n): if (i >> j) & 1: tmp_res.append(nums[j]) res.append(tmp_res) return res # 85. Maximal Rectangle b = [item[:] for item in a]
71. Simplify Path
双指针算法
class Solution: def simplifyPath(self, path: str) -> str: i, j = 0, 0 n = len(path) dir_names = [] while i < n: if path[i] == '/': while i < n and path[i] == '/': i += 1 else: j = i + 1 while j < n and path[j] != '/': j += 1 dir_names.append(path[i:j]) i = j new_dir = [] for name in dir_names: if name == '.': continue elif name == '..': if new_dir: new_dir.pop() else: new_dir.append(name) return '/' + '/'.join(new_dir)
利用库函数的精简代码
class Solution: def simplifyPath(self, path: str) -> str: new_dir = [] for name in path.split('/'): if name not in ['.', '..', '']: new_dir.append(name) if name == '..': if new_dir: new_dir.pop() return '/' + '/'.join(new_dir)
72. Edit Distance
入门动态规划,最短编辑距离
# Dp class Solution: def minDistance(self, a: str, b: str) -> int: # define and initialize n, m = len(a), len(b) a = ' ' + a b = ' ' + b f = [[0] * (m + 1) for i in range(n + 1)] for j in range(0, m + 1): f[0][j] = j for i in range(0, n + 1): f[i][0] = i # dynamic programming for i in range(1, n + 1): for j in range(1, m + 1): if a[i] == b[j]: f[i][j] = f[i - 1][j - 1] else: f[i][j] = min(f[i - 1][j - 1], min(f[i - 1][j], f[i][j - 1])) + 1 return f[n][m]
73. Set Matrix Zeroes
一定遇到原地操作,空间复杂度O(0)的时候,主要是应用好原本的数组,仅仅开辟常数级别的变量
class Solution: def setZeroes(self, matrix: List[List[int]]) -> None: """ Do not return anything, modify matrix in-place instead. """ flag_0_row, flag_0_column = False, False n, m = len(matrix), len(matrix[0]) for i in range(n): for j in range(m): if matrix[i][j] == 0: matrix[i][0] = 0 matrix[0][j] = 0 if i == 0: flag_0_row = True if j == 0: flag_0_column = True for i in range(1, n): if matrix[i][0] == 0: for j in range(m): matrix[i][j] = 0 for j in range(1, m): if matrix[0][j] == 0: for i in range(n): matrix[i][j] = 0 if flag_0_row: for j in range(m): matrix[0][j] = 0 if flag_0_column: for i in range(n): matrix[i][0] = 0
74. Search a 2D Matrix
简单的二分问题,先 row 二分,然后 col 二分即可,查找是否存在 target 数值
class Solution: def searchMatrix(self, matrix: List[List[int]], target: int) -> bool: n, m = len(matrix), len(matrix[0]) l, r = 0, n - 1 if matrix[l][0] > target: return False else: # 寻找 matrix[XXX][0] <= target 的最大值 while l < r: mid = (l + r + 1) // 2 if matrix[mid][0] <= target: l = mid else: r = mid - 1 # 寻找 matrix[x][XXX] <= target 的最大值 x = l l, r = 0, m - 1 while l < r: mid = (l + r + 1) // 2 if matrix[x][mid] <= target: l = mid else: r = mid - 1 return matrix[x][l] == target
也可以直接一维判断,不过好手动转化为二维数组
class Solution: def searchMatrix(self, matrix: List[List[int]], target: int) -> bool: n, m = len(matrix), len(matrix[0]) l, r = 0, n * m - 1 while l <= r: mid = (l + r) // 2 if matrix[mid // m][mid % m] == target: return True elif matrix[mid // m][mid % m] > target: r = mid - 1 else: l = mid + 1 return False
75. Sort Colors
# 快排 # 或者是直接进行数数 class Solution: def sortColors(self, nums: List[int]) -> None: """ Do not return anything, modify nums in-place instead. """ cnt, n = [0] * 3, len(nums) for i in range(n): cnt[nums[i]] += 1 sum = 0 for k in range(3): for i in range(sum, sum + cnt[k]): nums[i] = k sum += cnt[k]
如果是只想扫描一遍的话,可以按照双指针算法来写
有 0 就从前往后放,有 2 就从后往前放,剩下的就是 1的地方,不过这样放置的话,可能会覆盖一个数组单元,因此需要那一个临时变量存储一下子,这里我使用的 t = nums[n - 1] 来防止 nums[[n - 1]未遍历前就被覆盖了
class Solution: def sortColors(self, nums: List[int]) -> None: """ Do not return anything, modify nums in-place instead. """ n = len(nums) i, j = 0, n - 2 t = nums[n - 1] cnt = 0 pos_0, pos_2 = 0, n - 1 flag = True while i <= j: if flag: if nums[i] == 0: nums[pos_0] = 0 pos_0 += 1 elif nums[i] == 2: nums[pos_2] = 2 pos_2 -= 1 flag = False i += 1 else: if nums[j] == 0: nums[pos_0] = 0 pos_0 += 1 flag = True elif nums[j] == 2: nums[pos_2] = 2 pos_2 -= 1 j -= 1 if t == 0: nums[pos_0] = 0 pos_0 += 1 elif t == 2: nums[pos_2] = 2 pos_2 -= 1 for i in range(pos_0, pos_2 + 1): nums[i] = 1
前面两个是自己想着玩的,看到别人有一个做法,看起来和第二种类似。
首先对于数字2,也是放在后面,不过选择了 swap,也就是说便利时候,只看了 i指针的增加。
并且针对 0、1分界的问题,也是通过 swap 解决
图示
class Solution: def sortColors(self, nums: List[int]) -> None: """ Do not return anything, modify nums in-place instead. """ n = len(nums) i, j, k = 0, -1, n # i 指向的是当前遍历的点,j指向的是 第一个 1所存储的位置 while i != k: if nums[i] == 0: nums[j + 1], nums[i] = nums[i], nums[j + 1] j += 1 i += 1 elif nums[i] == 2: nums[i], nums[k - 1] = nums[k - 1], nums[i] k -= 1 else: i += 1
76. Minimum Window Substring
使用双指针算法,有点类似于滑动区间的感觉
i指针负责前面移动,j指针负责尾部移动, i += 1 之后,查看 j 是否可以往前移动,这需要当前字符串字典的打表,查看是否满足移动的要求
class Solution: def minWindow(self, s: str, t: str) -> str: # 统计 t sum_cnt = {} for item in t: if item in sum_cnt: sum_cnt[item] += 1 else: sum_cnt[item] = 1 # 查看 s i, j = 0, 0 res_st, res_ed = -1, -1 target = len(sum_cnt) cur_num = 0 n = len(s) cur_cnt = {} while i < n: if s[i] in sum_cnt: # 更新 cur_cnt[s[i]] if s[i] not in cur_cnt: cur_cnt[s[i]] = 1 else: cur_cnt[s[i]] += 1 if cur_cnt[s[i]] == sum_cnt[s[i]]: cur_num += 1 # 查看 j 是否可以后退 while j <= i and (s[j] not in sum_cnt or cur_cnt[s[j]] > sum_cnt[s[j]]): if s[j] in sum_cnt: cur_cnt[s[j]] -= 1 j += 1 # 更新 i 指针 i += 1 # print(f'i={i}, j={j}, cur_num={cur_num}') # print(cur_cnt) # 更新答案 if cur_num == target and (res_ed - res_st >= i - j or res_ed == -1): res_ed, res_st = i, j print(f'st={res_st}, ed={res_ed}') if res_ed == -1: return '' else: return s[res_st:res_ed]
python 的 dict 在处理这里有些慢,这里我们尝试使用 Counter
class Solution: def minWindow(self, s: str, t: str) -> str: # 统计 t need_cnt = Counter() for item in t: need_cnt[item] += 1 # 查看 s target = len(need_cnt) cur_num = 0 i, j = 0, 0 res_st, res_ed = -1, -1 n = len(s) while i < n: if s[i] in need_cnt: need_cnt[s[i]] -= 1 if need_cnt[s[i]] == 0: cur_num += 1 i += 1 while j < i and (s[j] not in need_cnt or need_cnt[s[j]] < 0): if s[j] in need_cnt: need_cnt[s[j]] += 1 j += 1 if cur_num == target and (res_ed == -1 or res_ed - res_st > i - j): res_ed, res_st = i, j if res_ed == -1: return '' else: return s[res_st:res_ed]
77. Combinations
DFS
class Solution: tmp_res = [] res = [] def dfs(self, cur_pos, cur_num, k, n): if cur_pos == k + 1: self.res.append(self.tmp_res[:]) return for i in range(cur_num, n + 1): if n + 1 - i < k - cur_pos + 1: break self.tmp_res.append(i) self.dfs(cur_pos + 1, i + 1, k, n) self.tmp_res.pop() def combine(self, n: int, k: int) -> List[List[int]]: self.tmp_res, self.res = [], [] self.dfs(1, 1, k, n) return self.res
78. Subsets
DFS
class Solution: res, tmp_res = [], [] def dfs(self, cur, nums): if cur == len(nums): self.res.append(self.tmp_res[:]) return else: self.dfs(cur + 1, nums) self.tmp_res.append(nums[cur]) self.dfs(cur + 1, nums) self.tmp_res.pop() def subsets(self, nums: List[int]) -> List[List[int]]: self.res, self.tmp_res = [], [] self.dfs(0, nums) return self.res
二进制
class Solution: def subsets(self, nums: List[int]) -> List[List[int]]: n, res = len(nums), [] for i in range(1 << n): tmp_res = [] for j in range(n): if (i >> j) & 1: tmp_res.append(nums[j]) res.append(tmp_res) return res
79. Word Search
还是得 DFS, BFS 发现不够用
DFS 代码,发现执行用时太长,考虑DP
class Solution: st = [] word = '' a = [] dx, dy = [1, 0, -1, 0], [0, 1, 0, -1] def dfs(self, x, y, cur_len): if cur_len == len(self.word): return True if x < 0 or y < 0 or x >= len(self.a) or y >= len(self.a[0]) or self.st[x][y]: return False if self.word[cur_len] == self.a[x][y]: self.st[x][y] = True for i in range(4): nx, ny = x + self.dx[i], y + self.dy[i] if self.dfs(nx, ny,cur_len + 1): return True self.st[x][y] = False return False def exist(self, board: List[List[str]], a: str) -> bool: n, m = len(board), len(board[0]) self.a = board self.word = a self.st = [[False] * m for i in range(n)] # 不必次次初始化 for i in range(n): for j in range(m): if a[0] == board[i][j]: if self.dfs(i, j, 0): return True return False
80. Remove Duplicates from Sorted Array II
class Solution: def removeDuplicates(self, nums: List[int]) -> int: if len(nums) <= 2: return len(nums) pre_val, cnt = 10101010, 0 j, n = 0, len(nums) for i in range(0, n): if nums[i] == pre_val: cnt += 1 if cnt <= 2: nums[j] = nums[i] j += 1 else: cnt = 1 pre_val = nums[i] nums[j] = nums[i] j += 1 return j
81. Search in Rotated Sorted Array II
他和Search in Rotated Sorted Array I有个很大的区别,在于二分需要寻找区间性质
前半部分满足一个性质,后半部分满足一个性质,然后可以二分出中间分界点。
但是因为他value 不是 惟一的,前后区间无法彻底满足这个区间特性。因此我们需要暴力一个 for 循环
举例子,前半部分 nums[] >= nums[0], 后半部分 nums[] <= nums[0]
但是因为他们都具有相等的这个属性,没办法找分界点。因此可以将前面的区间等号范围for枚举掉,也可以将后半部分区间的等号部分 枚举掉。
然后就可以折半查找
class Solution: def get(self, l, r, target, nums): if target < nums[l] or target > nums[r]: # 不要写 0, -1 return False while l <= r: mid = (l + r) >> 1 # print(f'l={l}, r={r}, mid={mid}, nums[mid]={nums[mid]}') if nums[mid] == target: return True elif nums[mid] > target: r = mid - 1 else: l = mid + 1 return False def search(self, nums: List[int], target: int) -> bool: # 我们首先应该寻找 nums 中 小于等于 nums[0] 的最大值 # 但是这个 l, r首先进行 确定 l 的数值 l, n = 1, len(nums) while l < n and nums[l] == nums[0]: l += 1 if l >= n: # 全为 nums[0] return nums[0] == target else: if nums[-1] > nums[0]: # 数组整个为递增 return self.get(0, len(nums) - 1, target, nums) r = len(nums) - 1 while l < r: mid = (l + r) >> 1 if nums[mid] <= nums[0]: r = mid else: l = mid + 1 # print(f'l={l}, nums[l]={nums[l]}') if nums[-1] >= target: return self.get(l, len(nums) - 1, target, nums) else: return self.get(0, l - 1, target, nums)
82. Remove Duplicates from Sorted List II
就是一个指针,不过自己创建一个头结点dummy 会方便很多
# Definition for singly-linked list. # class ListNode: # def __init__(self, val=0, next=None): # self.val = val # self.next = next class Solution: def deleteDuplicates(self, head: ListNode) -> ListNode: if not head or not head.next: return head dummy = ListNode(1011, head) pre_val = head.val p0, p1, p2 = dummy, head, head.next while p2: if p2.val == pre_val: while p2 and p2.val == pre_val: p2 = p2.next p0.next = p2 p1 = p2 if p2: pre_val = p2.val p2 = p2.next else: pre_val = p2.val p0, p1 = p1, p2 p2 = p2.next return dummy.next
83. Remove Duplicates from Sorted List
# Definition for singly-linked list. # class ListNode: # def __init__(self, val=0, next=None): # self.val = val # self.next = next class Solution: def deleteDuplicates(self, head: ListNode) -> ListNode: if not head: return head p = head.next pre_p = head while p: if p.val != pre_p.val: pre_p.next = p pre_p = p p = p.next else: pre_p.next = None # 应当给他绝掉 p = p.next return head
84. Largest Rectangle in Histogram
85. Maximal Rectangle
本来以为是 DP,然后一想发现直接统计个数好像就可以
(x1, y1), (x2, y2)
可以通过枚举 x1, y1, x2,然后二分查找合适的 y2
class Solution: def maximalRectangle(self, matrix: List[List[str]]) -> int: def get(x1, y1, x2, y2): return b[x2][y2] - (b[x2][y1-1] if y1 else 0) -\ (b[x1-1][y2] if x1 else 0) + (b[x1-1][y1-1] if x1 and y1 else 0) n, m = len(matrix), len(matrix[0]) a = [[1 if matrix[i][j] == '1' else 0 for j in range(m)] for i in range(n)] b = [item[:] for item in a] for i in range(n): for j in range(m): b[i][j] = b[i][j] + (b[i-1][j] if i else 0) + (b[i][j-1] if j else 0) - (b[i-1][j-1] if i and j else 0) res = 0 for x1 in range(n): for y1 in range(m): if a[x1][y1]: if get(x1, y1, n - 1, m - 1) == (n - 1 - x1 + 1) * (m - 1 - y1 + 1): res = max(res, (n - 1 - x1 + 1) * (m - 1 - y1 + 1)) break for x2 in range(x1, n): if a[x2][y1] == 0: # 加速一下子 break l, r = y1, m - 1 while l < r: mid = (l + r + 1) >> 1 if a[x2][mid] and get(x1, y1, x2, mid) == (x2 - x1 + 1) * (mid - y1 + 1): l = mid else: r = mid - 1 # print(f'({x1}, {y1}), ({x2}, {l})') res = max(res, (x2 - x1 + 1) * (l - y1 + 1)) return res
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· winform 绘制太阳,地球,月球 运作规律
· AI与.NET技术实操系列(五):向量存储与相似性搜索在 .NET 中的实现
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)