classSolution(object):
deflongestValidParentheses(self, s):
"""
:type s: str
:rtype: int
"""
stack = []
ans = 0
stack.append(-1)
for i inrange(len(s)):
if s[i] == '(':
stack.append(i)
else:
stack.pop()
if stack:
ans = max(ans, i - stack[-1])
else:
stack.append(i)
return ans
647. 回文子串【中等】
思路:中心扩展法,分奇数和偶数长度的回文处理。
classSolution(object):
defcountSubstrings(self, s):
"""
:type s: str
:rtype: int
"""
n = len(s)
defsolve(left, right):
cnt = 0while0 <= left < n and0 <= right < n and s[left] == s[right]:
cnt += 1
left -= 1
right += 1return cnt
ans = 0for i inrange(n):
ans += solve(i, i) # 奇数长度回文
ans += solve(i, i + 1) # 偶数长度回文return ans
139. 单词拆分【中等】
思路:动态规划。dp[j] 表示字符串前 j 个字符是否能被拆分。
classSolution:
defwordBreak(self, s: str, wordDict: List[str]) -> bool:
n = len(s)
dp = [True] + [False] * n
for i inrange(n):
for j inrange(i + 1, n + 1):
if dp[i] and s[i:j] in wordDict:
dp[j] = Truereturn dp[-1]
394. 字符串解码【中等】
思路:用栈去逐步提取字母和数字,处理嵌套结构。
classSolution:
defdecodeString(self, s: str) -> str:
stack = []
for c in s:
if c != ']':
stack.append(c)
if c == ']':
chars = ''while stack and stack[-1] != '[':
chars = stack[-1] + chars
stack.pop()
stack.pop() # 弹出 '['
num = ''while stack and stack[-1].isdigit():
num = stack[-1] + num
stack.pop()
num = int(num)
chars_repeat = num * chars
stack.append(chars_repeat)
ans = ''while stack:
ans = stack[-1] + ans
stack.pop()
return ans
72. 编辑距离【中等】
思路:DP。dp[i][j] 表示 word1 前 i 个字符变为 word2 前 j 个字符的操作数。
classSolution:
defminDistance(self, word1: str, word2: str) -> int:
m, n = len(word1), len(word2)
dp = [[0] * (n + 1) for _ inrange(m + 1)]
for i inrange(m + 1):
dp[i][0] = i
for i inrange(n + 1):
dp[0][i] = i
for i inrange(1, m + 1):
for j inrange(1, n + 1):
if word1[i - 1] == word2[j - 1]:
dp[i][j] = dp[i - 1][j - 1]
else:
dp[i][j] = min(dp[i][j - 1], dp[i - 1][j], dp[i - 1][j - 1]) + 1return dp[m][n]
classSolution:
defaddStrings(self, num1: str, num2: str) -> str:
ans = ""
carry = 0
i, j = len(num1) - 1, len(num2) - 1while i >= 0or j >= 0or carry:
x = int(num1[i]) if i >= 0else0
y = int(num2[j]) if j >= 0else0
sum_val = x + y + carry
value = sum_val % 10
carry = sum_val // 10
ans = str(value) + ans
i -= 1
j -= 1return ans
搜索查找
448. 找到所有数组中消失的数字【简单】
思路:不能用 python 的 if i in list,因为底层也是循环,会超时。最简单的做法是用辅助的 flag 来记录哪些位置有元素。
classSolution:
deffindDisappearedNumbers(self, nums: List[int]) -> List[int]:
n = len(nums)
ans = []
flag = [0] * (n + 1)
for x in nums:
flag[x] = 1for i inrange(1, n + 1):
if flag[i] == 0:
ans.append(i)
return ans
136. 只出现一次的数字【简单】
思路:集合 set 去重,然后用两倍 sum 减原来 sum 得到那个单个数。
classSolution(object):
defsingleNumber(self, nums):
"""
:type nums: List[int]
:rtype: int
"""
nums_set = set(nums)
nums_set_sum = sum(nums_set)
gap = nums_set_sum * 2 - sum(nums)
return gap
704. 二分查找【简单】
思路:标准二分查找模板。
classSolution(object):
defsearch(self, nums, target):
"""
:type nums: List[int]
:type target: int
:rtype: int
"""
left, right = 0, len(nums) - 1while left <= right:
mid = (left + right) // 2if nums[mid] == target:
return mid
elif nums[mid] > target:
right = mid - 1else:
left = mid + 1return -1
classSolution:
defmaxProfit(self, prices: List[int]) -> int:
n = len(prices)
ans = 0for i inrange(1, n):
ans += max(prices[i] - prices[i - 1], 0)
return ans
classSolution:
defmajorityElement(self, nums: List[int]) -> int:
n = len(nums)
threshold = n // 2
count_dict = {}
for num in nums:
if num in count_dict:
count_dict[num] += 1else:
count_dict[num] = 1
ans = 0
max_cnt = 0for k, v in count_dict.items():
if v >= threshold and v > max_cnt:
max_cnt = v
ans = k
return ans
739. 每日温度【中等】
思路:单调栈。最暴力的写法是 O(n^2) 的,会超时。
从右往左构造单调栈:
classSolution:
defdailyTemperatures(self, temperatures: List[int]) -> List[int]:
n = len(temperatures)
ans = [0] * n
stack = []
for i inrange(n - 1, -1, -1):
while stack and temperatures[i] >= temperatures[stack[-1]]:
stack.pop()
if stack:
ans[i] = stack[-1] - i
stack.append(i)
return ans
还可以从左往右构造单调栈:
classSolution:
defdailyTemperatures(self, temperatures: List[int]) -> List[int]:
n = len(temperatures)
ans = [0] * n
stack = []
for i inrange(n):
while stack and temperatures[i] > temperatures[stack[-1]]:
j = stack.pop()
ans[j] = i - j
stack.append(i)
return ans
1. 两数之和【简单】
思路:用 hash 存储。构建一个 hash 表,核心是判断 diff in hash_table。
classSolution:
deftwoSum(self, nums: List[int], target: int) -> List[int]:
n = len(nums)
hash_table = {}
for i inrange(n):
diff = target - nums[i]
if diff in hash_table:
return [hash_table[diff], i]
hash_table[nums[i]] = i
167. 两数之和 II - 输入有序数组【中等】
思路:利用数组单增的特点,采用相向双指针解决。
classSolution:
deftwoSum(self, numbers: List[int], target: int) -> List[int]:
n = len(numbers)
left, right = 0, n - 1while left < right:
s = numbers[left] + numbers[right]
if s < target:
left += 1elif s > target:
right -= 1else:
return [left + 1, right + 1]
15. 三数之和【中等】
思路:先排序,然后指定第一个元素,在剩下两个元素值上用相向双指针。
classSolution:
defthreeSum(self, nums: List[int]) -> List[List[int]]:
n = len(nums)
nums.sort()
ans = []
for i inrange(n - 2):
a = nums[i]
if i >= 1and a == nums[i - 1]:
continue
left, right = i + 1, n - 1while left < right:
s = a + nums[left] + nums[right]
if s < 0:
left += 1elif s > 0:
right -= 1elif s == 0:
ans.append([a, nums[left], nums[right]])
left += 1
right -= 1while left < right and nums[left] == nums[left - 1]:
left += 1while left < right and nums[right] == nums[right + 1]:
right -= 1return ans
classSolution:
deftopKFrequent(self, nums: List[int], k: int) -> List[int]:
n = len(nums)
hash_table = {}
for i inrange(n):
if nums[i] notin hash_table:
hash_table[nums[i]] = 1else:
hash_table[nums[i]] += 1
rank_value = sorted(hash_table.items(), key=lambda x: x[1], reverse=True)
ans = []
for i, (key, value) inenumerate(rank_value):
ans.append(key)
if i == k - 1:
breakreturn ans
287. 寻找重复数【中等】
思路:用 hash 存储遍历的值,如果发现重复则返回结果。
classSolution:
deffindDuplicate(self, nums: List[int]) -> int:
ifnot nums:
return -1
n = len(nums)
hash_table = set()
for i inrange(n):
if nums[i] in hash_table:
return nums[i]
hash_table.add(nums[i])
42. 接雨水【困难】
思路:相向双指针。记录左右两边的最高点,较低的那边去接雨水。
classSolution:
deftrap(self, height: List[int]) -> int:
n = len(height)
max_left, max_right = 0, 0
left, right = 0, n - 1
ans = 0while left < right:
max_left = max(max_left, height[left])
max_right = max(max_right, height[right])
if max_left <= max_right:
ans += max_left - height[left]
left += 1else:
ans += max_right - height[right]
right -= 1return ans
11. 盛最多水的容器【中等】
思路:相向双指针。面积取决于最矮的那边,所以哪边矮哪边就往前走一步。
classSolution:
defmaxArea(self, height: List[int]) -> int:
n = len(height)
if n == 2:
returnmin(height[0], height[1]) * 1
ans = 0
left, right = 0, n - 1while left < right:
width = right - left
cur_s = width * min(height[left], height[right])
ans = max(ans, cur_s)
if height[left] <= height[right]:
left += 1else:
right -= 1return ans
LCR 172. 统计目标成绩的出现次数【简单】
思路:二分法搜索边界。
classSolution:
defcountTarget(self, scores: List[int], target: int) -> int:
n = len(scores)
defhelper(t):
left, right = 0, n - 1while left <= right:
mid = (left + right) // 2if scores[mid] < t:
left = mid + 1else:
right = mid - 1return left
l = helper(target)
r = helper(target + 1)
return r - l
34. 在排序数组中查找元素的第一个和最后一个位置【中等】
思路:同第 172 题,二分法搜索边界。
classSolution:
defsearchRange(self, nums: List[int], target: int) -> List[int]:
ifnot nums:
return [-1, -1]
n = len(nums)
defhelper(t):
left, right = 0, n - 1while left <= right:
mid = (left + right) // 2if nums[mid] < t:
left = mid + 1else:
right = mid - 1return left
l = helper(target)
r = helper(target + 1)
if r > l:
return [l, r - 1]
return [-1, -1]
1287. 有序数组中出现次数超过 25% 的元素【简单】
思路:用 hash/数组计数。
classSolution:
deffindSpecialInteger(self, arr: List[int]) -> int:
n = len(arr)
if n == 1:
return arr[0]
thre = int(n * 0.25)
hash_table = {}
for a in arr:
if a notin hash_table:
hash_table[a] = 1else:
hash_table[a] += 1if hash_table[a] > thre:
return a
215. 数组中的第 K 个最大元素【中等】
思路:基于快速排序思想(Quick Select)。
classSolution:
deffindKthLargest(self, nums: List[int], k: int) -> int:
import random
defquick_select(nums, k):
pivot = random.choice(nums)
big, equal, small = [], [], []
for x in nums:
if x > pivot:
big.append(x)
elif x < pivot:
small.append(x)
else:
equal.append(x)
iflen(big) >= k:
return quick_select(big, k)
iflen(big) + len(equal) < k:
return quick_select(small, k - len(big) - len(equal))
return pivot
return quick_select(nums, k)
209. 长度最小的子数组【中等】
思路:滑动窗口,枚举右端点,收缩左端点,记录全局左端点和滑窗元素和。
classSolution:
defminSubArrayLen(self, target: int, nums: List[int]) -> int:
n = len(nums)
ans = n + 1
s = 0
left = 0for right, x inenumerate(nums):
s += x
while s >= target:
ans = min(ans, right - left + 1)
s -= nums[left]
left += 1return ans if ans <= n else0
76. 最小覆盖子串【困难】
思路:滑动窗口,和第 209 题思路一致,这里用到字符计数器 Count()。
from collections import Counter
classSolution:
defminWindow(self, s: str, t: str) -> str:
cnt_win = Counter()
cnt_t = Counter(t)
ans_left, ans_right = -float('inf'), float('inf')
left = 0for right, x inenumerate(s):
cnt_win[x] += 1while cnt_win >= cnt_t:
if right - left < ans_right - ans_left:
ans_left, ans_right = left, right
cnt_win[s[left]] -= 1
left += 1return s[ans_left:ans_right + 1] if ans_left >= 0else""
3. 无重复字符的最长子串【中等】
思路:滑动窗口,用 set 检测重复元素。
classSolution:
deflengthOfLongestSubstring(self, s: str) -> int:
n = len(s)
ans = 0
visited = set()
left = 0for right, x inenumerate(s):
while x in visited:
visited.remove(s[left])
left += 1
visited.add(x)
ans = max(ans, right - left + 1)
return ans
排列
冒泡排序【排序模板】
classSolution(object):
defsortArray(self, nums):
"""
:type nums: List[int]
:rtype: List[int]
"""
n = len(nums)
for i inrange(1, n):
for j inrange(0, n - i):
if nums[j] > nums[j + 1]:
nums[j], nums[j + 1] = nums[j + 1], nums[j]
return nums
快速排序【排序模板】
classSolution:
defsortArray(self, nums: List[int]) -> List[int]:
import random
defpartition(arr, low, high):
r = random.randint(low, high)
arr[r], arr[high] = arr[high], arr[r]
pivot = arr[high]
slow = fast = low
while fast <= high:
if arr[fast] < pivot:
arr[fast], arr[slow] = arr[slow], arr[fast]
slow += 1
fast += 1
arr[high], arr[slow] = arr[slow], arr[high]
return slow
defquicksort(arr, low, high):
if low < high:
pivot_idx = partition(arr, low, high)
quicksort(arr, low, pivot_idx - 1)
quicksort(arr, pivot_idx + 1, high)
return arr
n = len(nums)
return quicksort(nums, 0, n - 1)
283. 移动零【简单】
思路:双指针(都从左侧开始),实现 inplace 排列。
classSolution(object):
defmoveZeroes(self, nums):
"""
:type nums: List[int]
:rtype: None Do not return anything, modify nums in-place instead.
"""
left = 0for right inrange(len(nums)):
if nums[right] != 0:
nums[right], nums[left] = nums[left], nums[right]
left += 1return nums
如果用双指针(从两端开始),无法保证元素的相对顺序,不推荐。
当然也可以先把所有非零数放到左边,最后右边填补 0。
classSolution(object):
defmoveZeroes(self, nums):
"""
:type nums: List[int]
:rtype: None Do not return anything, modify nums in-place instead.
"""
idx_non_zero = 0for i inrange(len(nums)):
if nums[i] != 0:
nums[idx_non_zero] = nums[i]
idx_non_zero += 1for i inrange(idx_non_zero, len(nums)):
nums[i] = 0return nums
75. 颜色分类【中等】
思路:三指针,p0 左侧保持全 0,p2 右侧保持全 2,i 从左往右遍历数组。
细节:如果 i 遇到 0,swap 后 i 继续走;如果 i 遇到 2,swap 后 i 先别动(因为可能把后面的 0 交换到前面了)。
classSolution:
defsortColors(self, nums: List[int]) -> None:
n = len(nums)
p0, p2 = 0, n - 1
i = 0while i <= p2:
if nums[i] == 2:
nums[i], nums[p2] = nums[p2], nums[i]
p2 -= 1elif nums[i] == 0:
nums[i], nums[p0] = nums[p0], nums[i]
p0 += 1
i += 1elif nums[i] == 1:
i += 1
406. 根据身高重建队列【中等】
思路:对身高降序,k 升序,然后根据 k 进行插入。
classSolution:
defreconstructQueue(self, people: List[List[int]]) -> List[List[int]]:
n = len(people)
ans = []
people_sort = sorted(people, key=lambda x: (-x[0], x[1]))
for person in people_sort:
ans.insert(person[1], person)
return ans
977. 有序数组的平方【简单】
思路:相向双指针,从两端向中间比较平方值大小。
classSolution:
defsortedSquares(self, nums: List[int]) -> List[int]:
n = len(nums)
left, right = 0, n - 1
ans = []
while left <= right:
if nums[left] ** 2 <= nums[right] ** 2:
ans.append(nums[right] ** 2)
right -= 1else:
ans.append(nums[left] ** 2)
left += 1return ans[::-1]
31. 下一个排列【中等】
思路:先从右往左找到第一个较小元素,然后和右侧比其大一点的元素交换,再把右侧元素翻转为升序。
classSolution:
defnextPermutation(self, nums: List[int]) -> None:
n = len(nums)
i = n - 2while i >= 0and nums[i] >= nums[i + 1]:
i -= 1if i >= 0:
j = n - 1while nums[j] <= nums[i]:
j -= 1
nums[i], nums[j] = nums[j], nums[i]
left, right = i + 1, n - 1while left < right:
nums[left], nums[right] = nums[right], nums[left]
left += 1
right -= 1
组合
49. 字母异位词分组【中等】
思路:用字典的相同 key 映射多个字母异位词(用 value 列表存)。
classSolution(object):
defgroupAnagrams(self, strs):
"""
:type strs: List[str]
:rtype: List[List[str]]
"""
d = {}
for s in strs:
sorted_s = ''.join(sorted(s))
d[sorted_s] = d.get(sorted_s, []) + [s]
returnlist(d.values())
78. 子集【中等】
思路:维护一个 ans 列表,遍历 nums 中所有元素,每个元素都将和 ans 列表中每一项进行组合。
classSolution(object):
defsubsets(self, nums):
"""
:type nums: List[int]
:rtype: List[List[int]]
"""
ans = [[]]
for num in nums:
tmp = []
for i in ans:
tmp.append([num] + i)
ans += tmp
return ans
46. 全排列【中等】
思路:回溯法。
classSolution:
defpermute(self, nums: List[int]) -> List[List[int]]:
n = len(nums)
ans = []
defdfs(start):
if start == n:
ans.append(nums[:])
returnfor i inrange(start, n):
nums[i], nums[start] = nums[start], nums[i]
dfs(start + 1)
nums[i], nums[start] = nums[start], nums[i]
dfs(0)
return ans
47. 全排列 II【中等】
思路:仍然是第 46 题的回溯,但是需要用哈希去重。
classSolution:
defpermuteUnique(self, nums: List[int]) -> List[List[int]]:
n = len(nums)
ans = []
defdfs(start):
if start == n:
ans.append(nums[:])
return
dic = set()
for i inrange(start, n):
if nums[i] in dic:
continue
dic.add(nums[i])
nums[i], nums[start] = nums[start], nums[i]
dfs(start + 1)
nums[i], nums[start] = nums[start], nums[i]
dfs(0)
return ans
LCR 157. 套餐内商品的排列顺序【中等】
思路:同样回溯,但是针对字符串,需要先转 list 才能支持交换操作。
classSolution:
defgoodsOrder(self, goods: str) -> List[str]:
n = len(goods)
lst = list(goods)
ans = []
defdfs(start):
if start == n:
ans.append(''.join(lst))
returnfor i inrange(start, n):
lst[i], lst[start] = lst[start], lst[i]
dfs(start + 1)
lst[i], lst[start] = lst[start], lst[i]
dfs(0)
returnlist(set(ans))
classSolution(object):
defisPalindrome(self, head):
"""
:type head: Optional[ListNode]
:rtype: bool
"""
l = []
while head:
l.append(head.val)
head = head.nextreturn l == l[::-1]
160. 相交链表【简单】
思路:用 hash 存储一条链表的节点,然后去搜索另一条链表中哪个节点存在于 hash。
classSolution:
defgetIntersectionNode(self, headA: ListNode, headB: ListNode) -> Optional[ListNode]:
ifnot headA ornot headB:
returnNone
link_set = set()
cur = headA
while cur:
link_set.add(cur)
cur = cur.next
cur = headB
while cur:
if cur in link_set:
return cur
cur = cur.nextreturnNone
classSolution:
defhasCycle(self, head: Optional[ListNode]) -> bool:
ifnot head:
returnFalse
slow, fast = head, head
while fast and fast.next:
slow = slow.next
fast = fast.next.nextif slow == fast:
returnTruereturnFalse
classSolution:
defdetectCycle(self, head: Optional[ListNode]) -> Optional[ListNode]:
ifnot head:
returnNone
slow, fast = head, head
while fast and fast.next:
slow = slow.next
fast = fast.next.nextif slow == fast:
tmp = head
while tmp != slow:
slow = slow.next
tmp = tmp.nextreturn slow
returnNone
148. 排序链表【中等】
思路:归并排序。先分割成两组,再合并。
classSolution:
defsortList(self, head: Optional[ListNode]) -> Optional[ListNode]:
ifnot head ornot head.next:
return head
slow, fast = head, head.nextwhile fast and fast.next:
slow = slow.next
fast = fast.next.next
mid = slow.next
slow.next = None
left = self.sortList(head)
right = self.sortList(mid)
ans = ListNode()
cur = ans
while left and right:
if left.val < right.val:
cur.next = left
left = left.nextelse:
cur.next = right
right = right.next
cur = cur.nextif left:
cur.next = left
elif right:
cur.next = right
return ans.next
classSolution:
defremoveNthFromEnd(self, head: Optional[ListNode], n: int) -> Optional[ListNode]:
dummy = ListNode(next=head)
right = dummy
for _ inrange(n):
right = right.next
left = dummy
while right.next:
left = left.next
right = right.next
left.next = left.next.nextreturn dummy.next
83. 删除排序链表中的重复元素【简单】
思路:直接判断下一个节点是否值相同。
classSolution:
defdeleteDuplicates(self, head: Optional[ListNode]) -> Optional[ListNode]:
ifnot head:
returnNone
cur = head
while cur.next:
if cur.next.val == cur.val:
cur.next = cur.next.nextelse:
cur = cur.nextreturn head
classSolution:
defdeleteDuplicates(self, head: Optional[ListNode]) -> Optional[ListNode]:
dummy = ListNode(next=head)
cur = dummy
while cur.nextand cur.next.next:
val = cur.next.val
if cur.next.next.val == val:
while cur.nextand cur.next.val == val:
cur.next = cur.next.nextelse:
cur = cur.nextreturn dummy.next
203. 移除链表元素【简单】
思路:用哨兵节点,迭代判断。
classSolution:
defremoveElements(self, head: Optional[ListNode], val: int) -> Optional[ListNode]:
ifnot head:
returnNone
dummy = ListNode(next=head)
cur = dummy
while cur and cur.next:
if cur.next.val == val:
cur.next = cur.next.nextelse:
cur = cur.nextreturn dummy.next
2. 两数相加【中等】
思路:参照第 989 题的加法模板,该题从左往右即数字的低位开始。
classSolution:
defaddTwoNumbers(self, l1: Optional[ListNode], l2: Optional[ListNode]) -> Optional[ListNode]:
ans = ListNode(0)
cur = ans
carry = 0while l1 or l2:
x = l1.val if l1 else0
y = l2.val if l2 else0
s = x + y + carry
value = s % 10
carry = s // 10
cur.next = ListNode(value)
cur = cur.nextif l1: l1 = l1.nextif l2: l2 = l2.nextif carry:
cur.next = ListNode(carry)
return ans.next
21. 合并两个有序链表【简单】
思路:双指针,取较小的,并移动其指针。
classSolution:
defmergeTwoLists(self, list1: Optional[ListNode], list2: Optional[ListNode]) -> Optional[ListNode]:
ifnot list1 andnot list2:
returnNone
ans = ListNode()
cur = ans
while list1 and list2:
if list1.val < list2.val:
cur.next = list1
list1 = list1.nextelse:
cur.next = list2
list2 = list2.next
cur = cur.nextif list1:
cur.next = list1
elif list2:
cur.next = list2
return ans.next
23. 合并 K 个升序链表【困难】
思路:两两合并,分治处理。
classSolution:
defmergeKLists(self, lists: List[Optional[ListNode]]) -> Optional[ListNode]:
defmergeTwoLists(list1, list2):
dummy = ListNode()
cur = dummy
while list1 and list2:
if list1.val < list2.val:
cur.next = list1
list1 = list1.nextelse:
cur.next = list2
list2 = list2.next
cur = cur.nextif list1:
cur.next = list1
elif list2:
cur.next = list2
return dummy.next
n = len(lists)
if n == 0:
returnNoneif n == 1:
return lists[0]
left = self.mergeKLists(lists[:n // 2])
right = self.mergeKLists(lists[n // 2:])
return mergeTwoLists(left, right)
位运算
461. 汉明距离【简单】
思路:直接异或再计数,注意 bin() 的输出是 string。
classSolution(object):
defhammingDistance(self, x, y):
"""
:type x: int
:type y: int
:rtype: int
"""
xor = bin(x ^ y)
dist = list(xor[2:]).count('1')
return dist
338. 比特位计数【简单】
思路:转二进制然后计数。
classSolution(object):
defcountBits(self, n):
"""
:type n: int
:rtype: List[int]
"""
ans = []
for i inrange(n + 1):
bin_num = list(bin(i))[2:]
ans.append(bin_num.count('1'))
return ans
191. 位 1 的个数【简单】
思路:通过'与'进行逐位判断。
classSolution:
defhammingWeight(self, n: int) -> int:
bit_num = 32
one_bit = []
for i inrange(bit_num):
if n & (1 << i):
one_bit.append(1)
returnsum(one_bit)
231. 2 的幂【简单】
思路:转二进制后判断 1 的个数。
classSolution:
defisPowerOfTwo(self, n: int) -> bool:
if n <= 0:
returnFalse
bits = []
for i inrange(32):
cur_bit = 1if (2 ** i) & n else0
bits.append(cur_bit)
returnTrueif bits.count(1) == 1elseFalse
数组计算
238. 除自身以外数组的乘积【中等】
思路:分别计算某个数左右的乘积,再汇总起来。
classSolution:
defproductExceptSelf(self, nums: List[int]) -> List[int]:
n = len(nums)
front_mul = [1] + [0] * (n - 1)
for i inrange(1, n):
front_mul[i] = front_mul[i - 1] * nums[i - 1]
behind_mul = [0] * (n - 1) + [1]
for i inrange(n - 2, -1, -1):
behind_mul[i] = behind_mul[i + 1] * nums[i + 1]
ans = [0] * n
for i inrange(n):
ans[i] = front_mul[i] * behind_mul[i]
return ans
152. 乘积最大子数组【中等】
思路:考虑到正负号交替,需要同时存当前最大乘积和最小乘积。
classSolution:
defmaxProduct(self, nums: List[int]) -> int:
n = len(nums)
if n == 1:
return nums[0]
cur_max = [0] * n
cur_min = [0] * n
ans = nums[0]
cur_max[0], cur_min[0] = nums[0], nums[0]
for i inrange(n):
cur_max[i] = max(nums[i] * cur_min[i - 1], nums[i] * cur_max[i - 1], nums[i])
cur_min[i] = min(nums[i] * cur_min[i - 1], nums[i] * cur_max[i - 1], nums[i])
ans = max(ans, cur_max[i])
return ans
53. 最大子数组和【中等】
思路:这是 152 题的普通版,典型 DP 模板。
classSolution:
defmaxSubArray(self, nums: List[int]) -> int:
n = len(nums)
if n == 1:
return nums[0]
dp = [0] * n
dp[0] = nums[0]
for i inrange(n):
dp[i] = max(nums[i], dp[i - 1] + nums[i])
returnmax(dp)
300. 最长递增子序列【中等】
思路:DP。
classSolution:
deflengthOfLIS(self, nums: List[int]) -> int:
n = len(nums)
if n == 1:
return1
dp = [1] * n
for i inrange(n):
for j inrange(i):
if nums[i] > nums[j]:
dp[i] = max(dp[i], dp[j] + 1)
returnmax(dp)
128. 最长连续序列【中等】
思路:用 hash 存储方便查找元素,先找到序列的开头,然后不断 +1 去判断存在性。
classSolution:
deflongestConsecutive(self, nums: List[int]) -> int:
n = len(nums)
hash_set = set(nums)
ans = 0for num in hash_set:
if num - 1in hash_set:
continue
length_from_here = 1
cur_num = num
while cur_num + 1in hash_set:
cur_num += 1
length_from_here += 1
ans = max(ans, length_from_here)
return ans
438. 找到字符串中所有字母异位词【中等】
下面是完美的做法,每次滑窗实际上只需要更新左右两端两个元素的统计情况。
classSolution:
deffindAnagrams(self, s: str, p: str) -> List[int]:
window_size = len(p)
n = len(s)
ans = []
window_cnt = [0] * 26
p_cnt = [0] * 26for c in p:
p_cnt[ord(c) - ord('a')] += 1for c in s[:window_size]:
window_cnt[ord(c) - ord('a')] += 1if p_cnt == window_cnt:
ans.append(0)
for i inrange(1, n - window_size + 1):
left_del_char, right_new_char = s[i - 1], s[i + window_size - 1]
window_cnt[ord(left_del_char) - ord('a')] -= 1
window_cnt[ord(right_new_char) - ord('a')] += 1if p_cnt == window_cnt:
ans.append(i)
return ans
989. 数组形式的整数加法【简单】
思路:逐位计算模板。
classSolution:
defaddToArrayForm(self, num: List[int], k: int) -> List[int]:
n = len(num)
i = n - 1
carry = 0
ans = []
while i >= 0or k != 0:
x = num[i] if i >= 0else0
y = k % 10if k != 0else0
s = x + y + carry
value = s % 10
carry = s // 10
ans.append(value)
i -= 1
k //= 10if carry:
ans.append(carry)
return ans[::-1]
119. 杨辉三角 II【简单】
思路:递推每行的值,但是要注意每行首尾两个元素默认为 1。
classSolution:
defgetRow(self, rowIndex: int) -> List[int]:
row_num = rowIndex + 1
T = [[1] * (i + 1) for i inrange(row_num)]
for i inrange(row_num):
for j inrange(1, i):
T[i][j] = T[i - 1][j - 1] + T[i - 1][j]
return T[rowIndex]
198. 打家劫舍【中等】
思路:DP。
classSolution:
defrob(self, nums: List[int]) -> int:
n = len(nums)
if n == 1:
return nums[0]
if n == 2:
returnmax(nums[0], nums[1])
dp = [0] * n
dp[0] = nums[0]
dp[1] = max(nums[0], nums[1])
for i inrange(2, n):
dp[i] = max(dp[i - 1], dp[i - 2] + nums[i])
return dp[-1]
213. 打家劫舍 II【中等】
思路:DP,首尾两间不能同时偷,所以需要分情况讨论首尾偷哪个。
classSolution:
defrob(self, nums: List[int]) -> int:
n = len(nums)
if n == 1:
return nums[0]
if n == 2:
returnmax(nums[0], nums[1])
# 情况 1:如果第 0 天可以偷,第 n-1 天不能偷
dp = [0] * n
dp[0] = nums[0]
dp[1] = max(nums[0], nums[1])
for i inrange(2, n - 1):
dp[i] = max(dp[i - 1], dp[i - 2] + nums[i])
ans1 = dp[-2]
# 情况 2:第 n-1 天可以偷,第 0 天不能偷
dp[1] = nums[1]
dp[2] = max(nums[1], nums[2])
for i inrange(3, n):
dp[i] = max(dp[i - 1], dp[i - 2] + nums[i])
ans2 = dp[-1]
returnmax(ans1, ans2)
classSolution:
defmaxSlidingWindow(self, nums: List[int], k: int) -> List[int]:
n = len(nums)
ans = []
q = deque()
for i, x inenumerate(nums):
while q and x >= nums[q[-1]]:
q.pop()
q.append(i)
if i - q[0] >= k:
q.popleft()
if i >= k - 1:
ans.append(nums[q[0]])
return ans
56. 合并区间【中等】
思路:先排序左端点,然后从左往右依次判断包含关系。
classSolution:
defmerge(self, intervals: List[List[int]]) -> List[List[int]]:
intervals.sort(key=lambda p: p[0])
ans = []
for p in intervals:
if ans and p[0] <= ans[-1][1]:
ans[-1][1] = max(p[1], ans[-1][1])
else:
ans.append(p)
return ans
classSolution:
defnumIslands(self, grid: List[List[str]]) -> int:
defdfs(grid, r, c):
row_num = len(grid)
col_num = len(grid[0])
if r < 0or r > row_num - 1or c < 0or c > col_num - 1:
returnif grid[r][c] != "1":
return
grid[r][c] = "2"
dfs(grid, r + 1, c)
dfs(grid, r - 1, c)
dfs(grid, r, c + 1)
dfs(grid, r, c - 1)
row_num = len(grid)
col_num = len(grid[0])
ans = 0for r inrange(row_num):
for c inrange(col_num):
if grid[r][c] == '1':
dfs(grid, r, c)
ans += 1return ans
classSolution:
defsetZeroes(self, matrix: List[List[int]]) -> None:
rows = len(matrix)
cols = len(matrix[0])
row_flag, col_flag = [0] * rows, [0] * cols
for r inrange(rows):
for c inrange(cols):
if matrix[r][c] == 0:
row_flag[r] = 1
col_flag[c] = 1for r inrange(rows):
for c inrange(cols):
if row_flag[r] or col_flag[c]:
matrix[r][c] = 0
48. 旋转图像【中等】
思路:顺时针旋转 90 度=水平翻转 + 主对角线翻转(转置)。
classSolution:
defrotate(self, matrix: List[List[int]]) -> None:
n = len(matrix)
# 水平翻转for i inrange(n // 2):
for j inrange(n):
matrix[i][j], matrix[n - i - 1][j] = matrix[n - i - 1][j], matrix[i][j]
# 主对角线翻转for i inrange(n):
for j inrange(i):
matrix[i][j], matrix[j][i] = matrix[j][i], matrix[i][j]
867. 转置矩阵【简单】
思路:行列元素交换,注意不一定是方阵。
classSolution:
deftranspose(self, matrix: List[List[int]]) -> List[List[int]]:
rows = len(matrix)
cols = len(matrix[0])
ans = [[0] * rows for _ inrange(cols)]
for i inrange(rows):
for j inrange(cols):
ans[j][i] = matrix[i][j]
return ans
数学计算
69. x 的平方根【简单】
思路:二分法。
classSolution:
defmySqrt(self, x: int) -> int:
left, right = 0, x
ans = -1while left <= right:
mid = (left + right) // 2if mid ** 2 <= x:
ans = mid
left = mid + 1else:
right = mid - 1return ans
思路:牛顿迭代。
classSolution:
defmySqrt(self, x: int) -> int:
if x == 0:
return0
x0 = x
whileTrue:
x1 = 0.5 * (x0 + x / x0)
ifabs(x0 - x1) < 1e-6:
break
x0 = x1
returnint(x0)
构造随机变量,等概率生成 1~n 的数
题目:一个随机变量 X,有 P 的概率生成 1,(1-P)的概率生成 0,请利用 X 构造一个随机变量,等概率生成 1~n 的数。
解答:由题目可得,P(1)=p, P(0)=1-p。首先考虑一个更简单的问题,能否等概率生成 0 和 1?可以,随机采样两次。P(0,1)=(1-p)p, P(1,0)=p(1-p),这两种情况概率一致。
import random
defrandom_coin(p):
return1if random.random() < p else0defRand1(self):
i1 = random_coin()
i2 = random_coin()
if i1 == 0and i2 == 1:
return0elif i1 == 1and i2 == 0:
return1else:
return Rand1()
defRandN(self, n: int):
import numpy as np
k = int(np.log2(n) + 1)
res = ''for _ inrange(k):
res += str(Rand1())
ifint(res, 2) > n:
return RandN(n)
else:
returnint(res, 2)
343. 整数拆分【中等】
思路:DP。
classSolution:
defintegerBreak(self, n: int) -> int:
if n == 1:
return1
dp = [0] * (n + 1)
dp[1] = 1for i inrange(2, n + 1):
for j inrange(0, i):
dp[i] = max(dp[i], j * (i - j), j * dp[i - j])
return dp[n]
263. 丑数【简单】
思路:循环除 2、3、5,检查最后是否为 1。
classSolution:
defisUgly(self, n: int) -> bool:
if n <= 0:
returnFalsewhile n % 2 == 0:
n //= 2while n % 3 == 0:
n //= 3while n % 5 == 0:
n //= 5returnTrueif n == 1elseFalse
classSolution:
defaddDigits(self, num: int) -> int:
if num == 0:
return0while num >= 10:
s = 0while num > 0:
s += num % 10
num //= 10
num = s
return num
进阶:用 O(1) 复杂度完成,结果为模 9 的余数,但需要考虑 0 和 9 的倍数特殊情况。
classSolution:
defaddDigits(self, num: int) -> int:
if num == 0:
return0return (num - 1) % 9 + 1
202. 快乐数【简单】
思路:哈希判断重复元素。如果空间复杂度 O(1),则用快慢指针判断重复元素。
classSolution:
defisHappy(self, n: int) -> bool:
if n == 1:
returnTruedefcompute_next(n):
s = 0while n > 0:
s += (n % 10) ** 2
n //= 10return s
slow, fast = n, compute_next(n)
whileTrue:
if slow == fast:
returnFalseif fast == 1:
returnTrue
slow = compute_next(slow)
fast = compute_next(compute_next(fast))
图
207. 课程表【中等】
思路:用 DFS 判断图是否存在环。
classSolution:
defcanFinish(self, numCourses: int, prerequisites: List[List[int]]) -> bool:
graph = [[] for _ inrange(numCourses)]
for a, b in prerequisites:
graph[b].append(a)
states = [0] * numCourses
defdfs(x):
states[x] = 1for node in graph[x]:
if states[node] == 1or (states[node] == 0and dfs(node) == True):
returnTrue
states[x] = 2returnFalsefor i, s inenumerate(states):
if s == 0and dfs(i) == True:
returnFalsereturnTrue
64. 最小路径和【中等】
思路:DP。
classSolution:
defminPathSum(self, grid: List[List[int]]) -> int:
rows, cols = len(grid), len(grid[0])
dp = [[0] * cols for _ inrange(rows)]
dp[0][0] = grid[0][0]
for i inrange(1, rows):
dp[i][0] = dp[i - 1][0] + grid[i][0]
for j inrange(1, cols):
dp[0][j] = dp[0][j - 1] + grid[0][j]
for i inrange(1, rows):
for j inrange(1, cols):
dp[i][j] = min(dp[i][j - 1], dp[i - 1][j]) + grid[i][j]
return dp[rows - 1][cols - 1]