目录
一、每日一题-2831. 找出最长等值子数组
1.分组 + 滑动窗口
来自灵神题解(. - 力扣(LeetCode))。
class Solution:
def longestEqualSubarray(self, nums: List[int], k: int) -> int:
# 分组 + 滑动窗口
group = defaultdict(list)
for i, x in enumerate(nums):
# group[x].append(i)
group[x].append(i - len(group[x]))
ans = 0
for g in group.values():
if len(g) <= ans:
continue
left = 0
for right, v in enumerate(g):
# left <= right < n
# left < n
# if v - g[left] - right + left > k:
if v - g[left] > k:
left += 1
ans = max(ans, right - left + 1)
return ans
2.一次遍历
来自官方题解(. - 力扣(LeetCode))。
class Solution:
def longestEqualSubarray(self, nums: List[int], k: int) -> int:
# 一次遍历
# 寻找区间众数
ans = 0
cnt = collections.defaultdict(int)
left = 0
for right, x in enumerate(nums):
cnt[x] += 1
if right - left + 1 - cnt[nums[left]] > k:
cnt[nums[left]] -= 1
left += 1
# 以右端点更新,包含了左右等和左右不等的情况
# 而以左端点更新达不到这样的效果
# 可以看成左端点是变化的,而右端点没变
ans = max(ans, cnt[nums[right]])
return ans
二、1441. 用栈操作构建数组
模拟
class Solution:
def buildArray(self, target: List[int], n: int) -> List[str]:
# 模拟
# 遇见不一样的就push + pop
if target[-1] > n:
return []
ans = []
num = 1
for x in target:
while num < x:
ans.extend(["Push", "Pop"])
num += 1
ans.append("Push")
num += 1
return ans
三、844. 比较含退格的字符串
1.栈 + 模拟
class Solution:
def backspaceCompare(self, s: str, t: str) -> bool:
# 栈 + 模拟
n1, n2 = len(s), len(t)
st1, st2 = [0] * n1, [0] * n2
top1, top2 = 0, 0
for c in s:
if c == '#':
top1 = max(0, top1 - 1)
else:
st1[top1] = c
top1 += 1
for c in t:
if c == '#':
top2 = max(0, top2 - 1)
else:
st2[top2] = c
top2 += 1
if top1 != top2:
return False
for i in range(top1):
if st1[i] != st2[i]:
return False
return True
2.重构字符串
来自官方题解(. - 力扣(LeetCode))。但是有点慢。
class Solution:
def backspaceCompare(self, s: str, t: str) -> bool:
# 重构字符串
def bulid(s: str) -> str:
st = []
for c in s:
if c != '#':
st.append(c)
elif st:
st.pop()
return "".join(st)
return bulid(s) == bulid(t)
3.快慢指针
来自题解(. - 力扣(LeetCode))。很妙!
class Solution:
def backspaceCompare(self, s: str, t: str) -> bool:
# 快慢指针
# 优化速度和存储空间
# 从后向前遍历
i, j = len(s) - 1, len(t) - 1
skipS, skipT = 0, 0
while i >= 0 or j >= 0:
# 外循环,判断对应位置是否相等
while i >= 0:
# 内循环,跳过特殊情况
if s[i] == '#':
skipS += 1
i -= 1
elif skipS > 0:
skipS -= 1
i -= 1
else:
break
while j >= 0:
if t[j] == '#':
skipT += 1
j -= 1
elif skipT > 0:
skipT -= 1
j -= 1
else:
break
# 判断
if i >= 0 and j >= 0:
if s[i] != t[j]:
return False
elif i >= 0 or j >= 0:
return False
i -= 1
j -= 1
return True
四、378. 有序矩阵中第 K 小的元素
1.直接排序-堆排序
没有利用性质,时复最大。
class Solution:
def kthSmallest(self, matrix: List[List[int]], k: int) -> int:
# 直接排序-堆排序
q = [x for row in matrix for x in row]
heapq.heapify(q)
for _ in range(k - 1):
heapq.heappop(q)
return q[0]
2024.5.(25,26号)续:《
2.归并排序
利用每一横排的有序性。
class Solution:
def merge_sort(self, nums:List[List[int]]) -> List[int]:
n = len(nums)
if n == 0:
return []
if n == 1:
return nums[0]
mid = n // 2
left = self.merge_sort(nums[:mid])
right = self.merge_sort(nums[mid:])
res = []
i, j = 0, 0
# 左右列表长度
while i < len(left) and j < len(right):
if right[j] < left[i]:
res.append(right[j])
j += 1
else:
res.append(left[i])
i += 1
# 将剩下的添加到末尾
res += left[i:]
res += right[j:]
return res
def kthSmallest(self, matrix: List[List[int]], k: int) -> int:
# 归并排序
nums = self.merge_sort(matrix)
return nums[k - 1]
3.归并排序-最小堆实现
来自官方题解(. - 力扣(LeetCode))。通过将坐标一起入堆,更好追踪后节点。
class Solution:
def kthSmallest(self, matrix: List[List[int]], k: int) -> int:
# 最小堆实现归并排序
n = len(matrix)
q = [(matrix[i][0], i, 0) for i in range(n)]
# 将坐标一起存入,便于调用后面的元素
heapq.heapify(q) # 堆化
for _ in range(k - 1):
num, x, y = heapq.heappop(q)
# 将后节点入堆
if y != n - 1:
heapq.heappush(q, (matrix[x][y + 1], x, y + 1))
return q[0][0]
4.二分查找
来自官方题解(. - 力扣(LeetCode))。check函数是精髓,充分利用了该矩阵的性质,时复最小。
class Solution:
def kthSmallest(self, matrix: List[List[int]], k: int) -> int:
# 二分查找
n = len(matrix)
def check(mid):
i, j = n - 1, 0 # 左下角起步
cnt = 0
while i >= 0 and j < n:
if matrix[i][j] <= mid:
# 向右走并更新cnt
cnt += i + 1
j += 1
else:
# 向上走
i -= 1
return cnt >= k
l, r = matrix[0][0], matrix[n - 1][n - 1] # 注意这里是值
while l <= r:
mid = (l + r) // 2
if check(mid):
r = mid - 1
else:
l = mid + 1
return l
》2024.5.(25,26号)续
五、23. 合并 K 个升序链表
1.归并排序-递归
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, val=0, next=None):
# self.val = val
# self.next = next
class Solution:
def mergeKLists(self, lists: List[Optional[ListNode]]) -> Optional[ListNode]:
# 归并排序-递归
n = len(lists)
if n == 0:
return None
if n == 1:
# 边界条件
return lists[0]
left = self.mergeKLists(lists[:n // 2]) # 递归
right = self.mergeKLists(lists[n // 2:])
head = ListNode() # 返回链表
lose = ListNode() # 指向链表头,方便返回
lose.next = head
while left and right:
if left.val > right.val:
head.next = right
right = right.next
else:
head.next = left
left = left.next
# 挪动指针
head = head.next
# 剩下部分
if left:
head.next = left
else:
head.next = right
# lose -> head -> 表
return lose.next.next
2024.5.25续:《
2.最小堆
来自灵神题解(. - 力扣(LeetCode))。
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, val=0, next=None):
# self.val = val
# self.next = next
# 入堆时无法直接比较节点大小
ListNode.__lt__ = lambda a, b: a.val < b.val
'''
__lt__ 是 Python 中的一个特殊方法(magic method),用于定义对象之间的“小于”(less than)比较运算。它的名字来自于 "less than" 的缩写。在 Python 中,可以通过实现 __lt__ 方法来自定义对象的 < 比较行为。
'''
class Solution:
def mergeKLists(self, lists: List[Optional[ListNode]]) -> Optional[ListNode]:
# 最小堆
cur = dummy = ListNode() # 哨兵节点,指向头节点的前一个节点,类似于lose
h = [head for head in lists if head] # 头节点入堆
heapq.heapify(h) # 堆化
while h:
node = heapq.heappop(h)
if node.next:
# 下一个节点入堆
heapq.heappush(h, node.next)
# 连接
cur.next = node
cur = cur.next
return dummy.next
》2024.5.25续
完
感谢你看到这里!一起加油吧!