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

还是得 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
posted @   lucky_light  阅读(67)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· winform 绘制太阳,地球,月球 运作规律
· AI与.NET技术实操系列(五):向量存储与相似性搜索在 .NET 中的实现
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)
点击右上角即可分享
微信分享提示