1. 理解Hot100算法题的核心价值
LeetCode Hot100是算法面试准备过程中最经典的题库之一,这个精选列表包含了实际面试中出现频率最高的100道题目。61-70这组题目在难度和考察重点上具有典型代表性,涵盖了动态规划、回溯算法、二叉树操作等关键知识点。
我在准备算法面试时发现,Hot100的题目往往不是单纯考察某个算法,而是测试候选人将算法思想应用到具体问题中的能力。比如第62题不同路径就完美体现了动态规划的核心思想——将大问题分解为子问题,并存储中间结果避免重复计算。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. Hot100(61-70)题目概览与分类
2.1 题目列表与难度分布
这10道题目按顺序分别是:
-
- 旋转链表(中等)
-
- 不同路径(中等)
-
- 不同路径 II(中等)
-
- 最小路径和(中等)
-
- 有效数字(困难)
-
- 加一(简单)
-
- 二进制求和(简单)
-
- 文本左右对齐(困难)
-
- x的平方根(简单)
-
- 爬楼梯(简单)
从难度分布可以看出,这组题目以中等难度为主,穿插了几道简单和困难题目。特别值得注意的是,动态规划类题目在这部分占比很高(62、63、64、70),这也是面试中的重点考察方向。
2.2 题目分类与核心考点
我们可以将这些题目分为几个主要类别:
- 链表操作:旋转链表(61)
- 动态规划:不同路径(62)、不同路径II(63)、最小路径和(64)、爬楼梯(70)
- 字符串处理:有效数字(65)、二进制求和(67)、文本左右对齐(68)
- 数学运算:加一(66)、x的平方根(69)
3. 核心题目解析与Python实现
3.1 动态规划专题:不同路径(62)
这道题是理解动态规划思想的绝佳案例。题目要求计算从网格左上角到右下角的所有可能路径,其中每次只能向右或向下移动。
python复制def uniquePaths(m: int, n: int) -> int:
dp = [[1]*n for _ in range(m)]
for i in range(1, m):
for j in range(1, n):
dp[i][j] = dp[i-1][j] + dp[i][j-1]
return dp[-1][-1]
关键点解析:
- 初始化dp数组时第一行和第一列都设为1,因为沿着网格边缘只有一条路径
- 状态转移方程dp[i][j] = dp[i-1][j] + dp[i][j-1]体现了动态规划的核心思想
- 时间复杂度O(mn),空间复杂度O(mn),可以通过滚动数组优化到O(n)
3.2 链表操作:旋转链表(61)
这道题考察链表的基本操作和指针运用能力。题目要求将链表向右旋转k个位置。
python复制def rotateRight(head: ListNode, k: int) -> ListNode:
if not head or not head.next or k == 0:
return head
# 计算链表长度
n = 1
tail = head
while tail.next:
tail = tail.next
n += 1
k = k % n
if k == 0:
return head
# 找到新头节点的前驱
new_tail = head
for _ in range(n - k - 1):
new_tail = new_tail.next
new_head = new_tail.next
new_tail.next = None
tail.next = head
return new_head
注意事项:
- 需要处理k大于链表长度的情况,使用取模运算
- 找到新头节点后要正确断开链表并连接原头节点
- 时间复杂度O(n),空间复杂度O(1)
4. 高频考点:动态规划问题变种
4.1 不同路径II(63)与最小路径和(64)
这两道题是62题的进阶版本,加入了障碍物和路径权重的概念。
不同路径II的Python实现:
python复制def uniquePathsWithObstacles(obstacleGrid: List[List[int]]) -> int:
m, n = len(obstacleGrid), len(obstacleGrid[0])
dp = [[0]*n for _ in range(m)]
# 初始化第一行和第一列
dp[0][0] = 1 if obstacleGrid[0][0] == 0 else 0
for i in range(1, m):
dp[i][0] = dp[i-1][0] if obstacleGrid[i][0] == 0 else 0
for j in range(1, n):
dp[0][j] = dp[0][j-1] if obstacleGrid[0][j] == 0 else 0
for i in range(1, m):
for j in range(1, n):
if obstacleGrid[i][j] == 0:
dp[i][j] = dp[i-1][j] + dp[i][j-1]
return dp[-1][-1]
最小路径和的Python实现:
python复制def minPathSum(grid: List[List[int]]) -> int:
m, n = len(grid), len(grid[0])
dp = [[0]*n for _ in range(m)]
dp[0][0] = grid[0][0]
# 初始化第一行和第一列
for i in range(1, m):
dp[i][0] = dp[i-1][0] + grid[i][0]
for j in range(1, n):
dp[0][j] = dp[0][j-1] + grid[0][j]
for i in range(1, m):
for j in range(1, n):
dp[i][j] = min(dp[i-1][j], dp[i][j-1]) + grid[i][j]
return dp[-1][-1]
4.2 爬楼梯(70)的多种解法
这道看似简单的题目实际上有多种解法,体现了算法思维的多样性。
- 递归解法(时间复杂度O(2^n),不推荐):
python复制def climbStairs(n: int) -> int:
if n <= 2:
return n
return climbStairs(n-1) + climbStairs(n-2)
- 动态规划解法(时间复杂度O(n),空间复杂度O(n)):
python复制def climbStairs(n: int) -> int:
if n <= 2:
return n
dp = [0]*(n+1)
dp[1], dp[2] = 1, 2
for i in range(3, n+1):
dp[i] = dp[i-1] + dp[i-2]
return dp[n]
- 空间优化解法(时间复杂度O(n),空间复杂度O(1)):
python复制def climbStairs(n: int) -> int:
if n <= 2:
return n
a, b = 1, 2
for _ in range(3, n+1):
a, b = b, a+b
return b
- 矩阵快速幂解法(时间复杂度O(logn),适合非常大的n):
python复制def climbStairs(n: int) -> int:
def matrix_pow(mat, power):
result = [[1,0],[0,1]] # 单位矩阵
while power > 0:
if power % 2 == 1:
result = matrix_mult(result, mat)
mat = matrix_mult(mat, mat)
power //= 2
return result
def matrix_mult(a, b):
return [
[a[0][0]*b[0][0] + a[0][1]*b[1][0], a[0][0]*b[0][1] + a[0][1]*b[1][1]],
[a[1][0]*b[0][0] + a[1][1]*b[1][0], a[1][0]*b[0][1] + a[1][1]*b[1][1]]
]
if n <= 2:
return n
mat = [[1,1],[1,0]]
result = matrix_pow(mat, n-2)
return 2*result[0][0] + result[0][1]
5. 字符串处理难题解析
5.1 有效数字(65)
这道困难题目考察对字符串的精确处理和状态机思想的应用。题目要求判断一个字符串是否可以表示有效的十进制数字。
python复制def isNumber(s: str) -> bool:
# 定义状态转移表
states = [
{'blank': 0, 'sign': 1, 'digit': 2, '.': 3}, # 状态0:起始状态
{'digit': 2, '.': 3}, # 状态1:符号位后
{'digit': 2, '.': 4, 'e': 5, 'blank': 8}, # 状态2:数字后
{'digit': 4}, # 状态3:小数点后(前面无数字)
{'digit': 4, 'e': 5, 'blank': 8}, # 状态4:小数点后(前面有数字)
{'sign': 6, 'digit': 7}, # 状态5:指数符号e后
{'digit': 7}, # 状态6:指数符号后的符号位
{'digit': 7, 'blank': 8}, # 状态7:指数数字后
{'blank': 8} # 状态8:结束空白
]
current_state = 0
for c in s:
if c.isdigit():
key = 'digit'
elif c in '+-':
key = 'sign'
elif c == '.':
key = '.'
elif c.lower() == 'e':
key = 'e'
elif c == ' ':
key = 'blank'
else:
return False
if key not in states[current_state]:
return False
current_state = states[current_state][key]
# 只有状态2、4、7、8是合法的结束状态
return current_state in {2, 4, 7, 8}
5.2 文本左右对齐(68)
这道困难题目考察字符串处理的精确控制和边界条件处理能力。
python复制def fullJustify(words: List[str], maxWidth: int) -> List[str]:
result = []
line = []
line_length = 0
for word in words:
# 判断当前行能否容纳新单词
if line_length + len(word) + len(line) <= maxWidth:
line.append(word)
line_length += len(word)
else:
# 处理当前行
if len(line) == 1:
result.append(line[0].ljust(maxWidth))
else:
total_spaces = maxWidth - line_length
space_between = total_spaces // (len(line) - 1)
extra_spaces = total_spaces % (len(line) - 1)
line_str = line[0]
for i in range(1, len(line)):
spaces = space_between + (1 if i <= extra_spaces else 0)
line_str += ' ' * spaces + line[i]
result.append(line_str)
# 开始新行
line = [word]
line_length = len(word)
# 处理最后一行(左对齐)
if line:
last_line = ' '.join(line)
last_line = last_line.ljust(maxWidth)
result.append(last_line)
return result
6. 数学运算题目精解
6.1 x的平方根(69)
这道题有多种解法,展示了不同算法思想的应用。
- 二分查找法(时间复杂度O(logn)):
python复制def mySqrt(x: int) -> int:
if x < 2:
return x
left, right = 2, x // 2
while left <= right:
mid = left + (right - left) // 2
num = mid * mid
if num == x:
return mid
elif num < x:
left = mid + 1
else:
right = mid - 1
return right
- 牛顿迭代法(更快的收敛速度):
python复制def mySqrt(x: int) -> int:
if x < 2:
return x
x0 = x
x1 = (x0 + x / x0) / 2
while abs(x0 - x1) >= 1:
x0 = x1
x1 = (x0 + x / x0) / 2
return int(x1)
6.2 加一(66)与二进制求和(67)
这两道简单题目考察基本的数学运算和进位处理。
加一的Python实现:
python复制def plusOne(digits: List[int]) -> List[int]:
n = len(digits)
for i in range(n-1, -1, -1):
if digits[i] < 9:
digits[i] += 1
return digits
digits[i] = 0
return [1] + digits
二进制求和的Python实现:
python复制def addBinary(a: str, b: str) -> str:
result = []
carry = 0
i, j = len(a)-1, len(b)-1
while i >= 0 or j >= 0 or carry:
total = carry
if i >= 0:
total += int(a[i])
i -= 1
if j >= 0:
total += int(b[j])
j -= 1
result.append(str(total % 2))
carry = total // 2
return ''.join(reversed(result))
7. 解题技巧与面试策略
7.1 Hot100题目的通用解题思路
-
理解题目本质:不要被题目描述迷惑,要识别出背后的算法模式。比如不同路径系列题目本质都是动态规划。
-
从简单案例入手:先手动计算小规模的例子,找出规律后再编写代码。
-
考虑边界条件:空输入、极端值、特殊情况的处理往往决定面试成败。
-
优化空间复杂度:在完成基本解法后,考虑是否能用滚动数组等技巧优化空间使用。
7.2 面试中的表达技巧
-
明确思路再编码:先向面试官解释你的解题思路,获得认可后再开始编码。
-
边写边解释:在编写代码时,同步解释关键步骤的意图。
-
测试用例设计:完成编码后,主动提出要测试的案例,包括常规情况和边界情况。
-
复杂度分析:最后不要忘记分析算法的时间和空间复杂度。
7.3 常见错误与避免方法
-
动态规划初始化错误:特别注意dp数组的初始状态设置,这是最容易出错的地方。
-
链表操作指针丢失:在修改链表结构时,注意保存必要的指针引用。
-
字符串索引越界:处理字符串时总是检查索引是否有效。
-
整数溢出问题:虽然Python不用担心这个问题,但在其他语言中要注意大数处理。
