Leetcode的考察点和ACM不太一样,之前面试也有几次尴尬写不出的经历。
从2026.07.04开始把Hot100过一遍,因为前面几题其实很久以前提交过,所以这次倒着刷。
这轮试着用Python而不是C++!
每天两题写完差不多正好是9月日常实习。某一天的题太简单的话就多做几道!
100
有n + 1个整数,值域是[1, n],找出唯一的重复的数。
假设这个重复的数是x。定义cnt[i]为小于等于i的数的个数。
cnt[i] <= i 当 i <= x - 1时。
cnt[i] > i 当 i >= x 时。
所以二分出最小的满足cnt[i] > i的i就可以了。
class Solution: def findDuplicate(self, nums: List[int]) -> int: l = 1 r = len(nums) - 1 while l < r: mid = (l + r) >> 1;
cnt = 0 for num in nums: cnt += num <= mid
if cnt > mid: r = mid else: l = mid + 1
return l99
按字典序找出下一个排列。
比如[1, 2, 3]的下一个是[1, 3, 2],再下一个是[2, 1, 3]。
[1, 3, 5, 4, 2] -> [1, 4, 5, 3, 2] -> [1, 4, 2, 3, 5]。这是一个一般化过程。
从后往前看,如果有一个数破坏了递增,说明这个数移到后面去是可以提升字典序的(因为后面有比它大的数)。
那么把哪个数和它调换?因为要求下一个排列,肯定是比它大的最小的数。
换完以后容易发现现在递减的部分可以倒过来,进一步缩小字典序。
class Solution: def nextPermutation(self, nums: List[int]) -> None: n = len(nums)
i = n - 2 while i >= 0 and nums[i] >= nums[i + 1]: i -= 1
if i >= 0: j = n - 1 while nums[j] <= nums[i]: j -= 1 nums[i], nums[j] = nums[j], nums[i]
nums[i+1:] = nums[i+1:][::-1]98
不用sort排序一个只含有0, 1, 2的数组。
这个问题叫荷兰国旗问题(名字加深印象)!
p0维护下一个0的位置,它的左边都是0。p2维护下一个2的位置,它的右边都是2。i是当前检查的位置。
循环的不变量:[0, p0)都是 0,(p2, n - 1]都是 2,中间[p0, i)都是 1。
把2交换到后面去的时候要再检查一次当前位置,因为被换到前面来的数可能是0或2。
在这个算法里,一个数最多会被搬两次。
class Solution: def sortColors(self, nums: List[int]) -> None: p0, i, p2 = 0, 0, len(nums) - 1 while i <= p2: if nums[i] == 0: nums[i], nums[p0] = nums[p0], nums[i] p0 += 1 i += 1 elif nums[i] == 2: nums[i], nums[p2] = nums[p2], nums[i] p2 -= 1 else: i += 197
求一个数组里的绝对众数。
擂台法。同一个数字想成同一个门派,同门派就加一战斗力,不同门派就减一战斗力。绝对众数的门派肯定能站到最后。
class Solution: def majorityElement(self, nums: List[int]) -> int: ans, cnt = 0, 0 for num in nums: if cnt == 0: ans = num cnt += 1 else: cnt += 1 if num == ans else -1 return ans96
找出只出现一次的数字,保证其他数字都是两次。
老生常谈的异或。
class Solution: def singleNumber(self, nums: List[int]) -> int: ans = 0 for num in nums: ans = ans ^ num return ans95
编辑距离。
dp[n][m]代表只考虑word1前n个字母,word2前m个字母的答案。
第n个和第m个字母相等时,只需要考虑dp[n - 1][m - 1]这个子问题。
不相等时,插入、删除、替换对应三个子问题。
@cache起到记忆化搜索的作用,Python把这个功能内置了。
class Solution: def minDistance(self, word1: str, word2: str) -> int: n = len(word1) m = len(word2)
@cache def dfs(x, y): if x < 0: return y + 1 if y < 0: return x + 1 if word1[x] == word2[y]: return dfs(x - 1, y - 1)
return min(dfs(x - 1, y), dfs(x, y - 1), dfs(x - 1, y - 1)) + 1
return dfs(n - 1, m - 1)94
最长公共子序列。
熟悉Python的多维列表初始化和enumerate。
class Solution: def longestCommonSubsequence(self, text1: str, text2: str) -> int: n, m = len(text1), len(text2) dp = [[0] * (m + 1) for _ in range(n + 1)]
for i, x in enumerate(text1): for j, y in enumerate(text2): dp[i + 1][j + 1] = dp[i][j] + 1 if x == y else max(dp[i][j + 1], dp[i + 1][j])
return dp[n][m]93
最长回文子串。
经典的区间DP。
class Solution: def longestPalindrome(self, s: str) -> str: n = len(s) dp = [[False] * n for _ in range(n)]
for i in range(n): dp[i][i] = True
for i in range(n - 1): dp[i][i + 1] = True if s[i] == s[i + 1] else False
for length in range(3, n + 1): for l in range(n): r = l + length - 1 if r > n - 1: break if s[l] == s[r]: dp[l][r] = dp[l + 1][r - 1]
ans = 0 st = 0 for i in range(n): for j in range(i, n): if dp[i][j] and j - i + 1 > ans: ans = j - i + 1 st = i
return s[st:st + ans]92
从左上角走到右下角的最小权重和,只能向下和向右。
经典的简单DP。
class Solution: def minPathSum(self, grid: List[List[int]]) -> int: n, m = len(grid), len(grid[0]) dp = [[0] * m for _ in range(n)]
dp[0][0] = grid[0][0] for j in range(1, m): dp[0][j] = dp[0][j - 1] + grid[0][j]
for i in range(1, n): dp[i][0] = dp[i - 1][0] + grid[i][0]
for i in range(1, n): for j in range(1, m): dp[i][j] = min(dp[i - 1][j], dp[i][j - 1]) + grid[i][j]
return dp[n - 1][m - 1]91
从左上角走到右下角的不同路径数,只能向下和向右。
可以是简单DP。
也可以是组合数学,m行n列,因为只能向下和向右,肯定是走m - 1 + n - 1步,这些步数里有m - 1步是向下走。
数据量小的时候Python有现成的comb函数,数据量大的时候还是得上预处理阶乘和逆元的写法。
class Solution: def uniquePaths(self, m: int, n: int) -> int: return comb(m + n - 2, m - 1)