markdown复制## 1. 高频面试题的价值与学习方法
刷题是程序员准备技术面试的必经之路,而LeetCode Top 100面试高频题更是精华中的精华。这些题目被各大科技公司反复考察,掌握它们能显著提高面试通过率。我整理了前20道最高频题目的解题思路和代码实现,这些题目覆盖了数组、字符串、链表、动态规划等核心数据结构与算法。
为什么选择Top 100?根据我的面试经验,这些题目在Google、Meta、Amazon等大厂的面试中出现频率超过60%。比如两数之和(Two Sum)这道题,我在5场不同公司的面试中遇到了3次。掌握这些题目不仅能帮你解决具体问题,更能培养算法思维,举一反三应对变种题。
> 提示:不要死记硬背代码,理解算法思想才是关键。面试官常常会修改题目条件来考察你是否真正掌握。
## 2. 第1-5题详解
### 2.1 两数之和(Two Sum)
这是LeetCode的第一题,也是最经典的一道。给定一个整数数组nums和一个目标值target,找出和为target的两个数的下标。
暴力解法是双重循环遍历所有组合,时间复杂度O(n²)。更优解法是使用哈希表存储已访问元素,将时间复杂度降到O(n):
```python
def twoSum(nums, target):
seen = {}
for i, num in enumerate(nums):
complement = target - num
if complement in seen:
return [seen[complement], i]
seen[num] = i
return []
注意事项:
- 注意处理重复元素的情况
- 返回的下标顺序不影响结果正确性
- 确保算法能处理无解的情况
2.2 两数相加(Add Two Numbers)
这道链表题考察对指针操作的理解。给定两个非空链表表示的非负整数,每位数字逆序存储,返回两数之和的链表。
关键点在于处理进位和不同长度链表:
python复制def addTwoNumbers(l1, l2):
dummy = ListNode(0)
current = dummy
carry = 0
while l1 or l2 or carry:
sum_val = carry
if l1:
sum_val += l1.val
l1 = l1.next
if l2:
sum_val += l2.val
l2 = l2.next
carry = sum_val // 10
current.next = ListNode(sum_val % 10)
current = current.next
return dummy.next
2.3 无重复字符的最长子串(Longest Substring Without Repeating Characters)
滑动窗口的经典应用。使用哈希集合记录窗口中的字符,当遇到重复时移动左指针:
python复制def lengthOfLongestSubstring(s):
char_set = set()
left = 0
max_len = 0
for right in range(len(s)):
while s[right] in char_set:
char_set.remove(s[left])
left += 1
char_set.add(s[right])
max_len = max(max_len, right - left + 1)
return max_len
2.4 寻找两个正序数组的中位数(Median of Two Sorted Arrays)
这道hard题目考察二分查找的高级应用。关键思路是将问题转化为寻找第k小的元素:
python复制def findMedianSortedArrays(nums1, nums2):
def getKthElement(k):
index1, index2 = 0, 0
while True:
if index1 == m:
return nums2[index2 + k - 1]
if index2 == n:
return nums1[index1 + k - 1]
if k == 1:
return min(nums1[index1], nums2[index2])
newIndex1 = min(index1 + k // 2 - 1, m - 1)
newIndex2 = min(index2 + k // 2 - 1, n - 1)
pivot1, pivot2 = nums1[newIndex1], nums2[newIndex2]
if pivot1 <= pivot2:
k -= newIndex1 - index1 + 1
index1 = newIndex1 + 1
else:
k -= newIndex2 - index2 + 1
index2 = newIndex2 + 1
m, n = len(nums1), len(nums2)
totalLength = m + n
if totalLength % 2 == 1:
return getKthElement((totalLength + 1) // 2)
else:
return (getKthElement(totalLength // 2) + getKthElement(totalLength // 2 + 1)) / 2
2.5 最长回文子串(Longest Palindromic Substring)
动态规划解法定义dp[i][j]表示s[i..j]是否为回文:
python复制def longestPalindrome(s):
n = len(s)
dp = [[False] * n for _ in range(n)]
ans = ""
for l in range(n):
for i in range(n):
j = i + l
if j >= n:
break
if l == 0:
dp[i][j] = True
elif l == 1:
dp[i][j] = (s[i] == s[j])
else:
dp[i][j] = (s[i] == s[j] and dp[i+1][j-1])
if dp[i][j] and l + 1 > len(ans):
ans = s[i:j+1]
return ans
3. 第6-10题解析
3.1 Z字形变换(ZigZag Conversion)
这道字符串处理题需要找出字符排列的规律。按行读取时,字符的位置有固定模式:
python复制def convert(s, numRows):
if numRows == 1:
return s
rows = [""] * numRows
curRow = 0
goingDown = False
for c in s:
rows[curRow] += c
if curRow == 0 or curRow == numRows - 1:
goingDown = not goingDown
curRow += 1 if goingDown else -1
return "".join(rows)
3.2 整数反转(Reverse Integer)
注意处理溢出问题,这是面试官常问的边界条件:
python复制def reverse(x):
INT_MIN, INT_MAX = -2**31, 2**31 - 1
rev = 0
sign = -1 if x < 0 else 1
x = abs(x)
while x != 0:
digit = x % 10
x = x // 10
if rev > (INT_MAX - digit) // 10:
return 0
rev = rev * 10 + digit
return rev * sign
3.3 字符串转换整数(atoi)
实现atoi函数需要考虑多种边界情况:
python复制def myAtoi(s):
INT_MIN, INT_MAX = -2**31, 2**31 - 1
s = s.lstrip()
if not s:
return 0
sign = 1
if s[0] in '+-':
if s[0] == '-':
sign = -1
s = s[1:]
num = 0
for c in s:
if not c.isdigit():
break
num = num * 10 + int(c)
if num > INT_MAX:
break
num *= sign
if num < INT_MIN:
return INT_MIN
if num > INT_MAX:
return INT_MAX
return num
3.4 回文数(Palindrome Number)
不将整数转为字符串的解法更能展示算法能力:
python复制def isPalindrome(x):
if x < 0 or (x % 10 == 0 and x != 0):
return False
reverted = 0
while x > reverted:
reverted = reverted * 10 + x % 10
x //= 10
return x == reverted or x == reverted // 10
3.5 正则表达式匹配(Regular Expression Matching)
动态规划经典难题,dp[i][j]表示s前i个字符和p前j个字符是否匹配:
python复制def isMatch(s, p):
m, n = len(s), len(p)
dp = [[False] * (n + 1) for _ in range(m + 1)]
dp[0][0] = True
for j in range(1, n + 1):
if p[j - 1] == '*':
dp[0][j] = dp[0][j - 2]
for i in range(1, m + 1):
for j in range(1, n + 1):
if p[j - 1] == s[i - 1] or p[j - 1] == '.':
dp[i][j] = dp[i - 1][j - 1]
elif p[j - 1] == '*':
dp[i][j] = dp[i][j - 2]
if p[j - 2] == s[i - 1] or p[j - 2] == '.':
dp[i][j] |= dp[i - 1][j]
return dp[m][n]
4. 第11-15题精讲
4.1 盛最多水的容器(Container With Most Water)
双指针法的典型应用,理解为什么移动较矮的指针是正确的:
python复制def maxArea(height):
left, right = 0, len(height) - 1
max_area = 0
while left < right:
area = min(height[left], height[right]) * (right - left)
max_area = max(max_area, area)
if height[left] < height[right]:
left += 1
else:
right -= 1
return max_area
4.2 整数转罗马数字(Integer to Roman)
贪心算法的应用,从大到小使用罗马数字符号:
python复制def intToRoman(num):
val = [
(1000, "M"), (900, "CM"), (500, "D"), (400, "CD"),
(100, "C"), (90, "XC"), (50, "L"), (40, "XL"),
(10, "X"), (9, "IX"), (5, "V"), (4, "IV"), (1, "I")
]
res = []
for v, symbol in val:
if num == 0:
break
count = num // v
res.append(symbol * count)
num -= v * count
return "".join(res)
4.3 罗马数字转整数(Roman to Integer)
处理特殊规则(如IV表示4)是关键:
python复制def romanToInt(s):
roman = {'I':1, 'V':5, 'X':10, 'L':50, 'C':100, 'D':500, 'M':1000}
res = 0
prev = 0
for c in reversed(s):
curr = roman[c]
if curr < prev:
res -= curr
else:
res += curr
prev = curr
return res
4.4 最长公共前缀(Longest Common Prefix)
垂直扫描法效率较高:
python复制def longestCommonPrefix(strs):
if not strs:
return ""
for i in range(len(strs[0])):
c = strs[0][i]
for s in strs[1:]:
if i >= len(s) or s[i] != c:
return strs[0][:i]
return strs[0]
4.5 三数之和(3Sum)
排序加双指针的经典组合,注意去重处理:
python复制def threeSum(nums):
nums.sort()
res = []
n = len(nums)
for i in range(n - 2):
if i > 0 and nums[i] == nums[i - 1]:
continue
left, right = i + 1, n - 1
while left < right:
total = nums[i] + nums[left] + nums[right]
if total < 0:
left += 1
elif total > 0:
right -= 1
else:
res.append([nums[i], nums[left], nums[right]])
while left < right and nums[left] == nums[left + 1]:
left += 1
while left < right and nums[right] == nums[right - 1]:
right -= 1
left += 1
right -= 1
return res
5. 第16-20题实战
5.1 最接近的三数之和(3Sum Closest)
与三数之和类似,但需要记录最接近的和:
python复制def threeSumClosest(nums, target):
nums.sort()
n = len(nums)
closest = float('inf')
for i in range(n - 2):
left, right = i + 1, n - 1
while left < right:
total = nums[i] + nums[left] + nums[right]
if abs(total - target) < abs(closest - target):
closest = total
if total < target:
left += 1
elif total > target:
right -= 1
else:
return target
return closest
5.2 电话号码的字母组合(Letter Combinations of a Phone Number)
回溯算法的入门题:
python复制def letterCombinations(digits):
if not digits:
return []
phone = {
'2': 'abc',
'3': 'def',
'4': 'ghi',
'5': 'jkl',
'6': 'mno',
'7': 'pqrs',
'8': 'tuv',
'9': 'wxyz'
}
res = []
def backtrack(index, path):
if len(path) == len(digits):
res.append("".join(path))
return
for c in phone[digits[index]]:
path.append(c)
backtrack(index + 1, path)
path.pop()
backtrack(0, [])
return res
5.3 四数之和(4Sum)
在三数之和基础上再套一层循环:
python复制def fourSum(nums, target):
nums.sort()
n = len(nums)
res = []
for i in range(n - 3):
if i > 0 and nums[i] == nums[i - 1]:
continue
for j in range(i + 1, n - 2):
if j > i + 1 and nums[j] == nums[j - 1]:
continue
left, right = j + 1, n - 1
while left < right:
total = nums[i] + nums[j] + nums[left] + nums[right]
if total < target:
left += 1
elif total > target:
right -= 1
else:
res.append([nums[i], nums[j], nums[left], nums[right]])
while left < right and nums[left] == nums[left + 1]:
left += 1
while left < right and nums[right] == nums[right - 1]:
right -= 1
left += 1
right -= 1
return res
5.4 删除链表的倒数第N个节点(Remove Nth Node From End of List)
双指针技巧的经典应用:
python复制def removeNthFromEnd(head, n):
dummy = ListNode(0)
dummy.next = head
fast = slow = dummy
for _ in range(n + 1):
fast = fast.next
while fast:
fast = fast.next
slow = slow.next
slow.next = slow.next.next
return dummy.next
5.5 有效的括号(Valid Parentheses)
栈数据结构的教科书级案例:
python复制def isValid(s):
stack = []
mapping = {')': '(', '}': '{', ']': '['}
for char in s:
if char in mapping:
top = stack.pop() if stack else '#'
if mapping[char] != top:
return False
else:
stack.append(char)
return not stack
6. 刷题技巧与面试准备
6.1 高频题目的共同特点
通过分析这20道高频题,我发现它们有几个共同特征:
- 都考察基础数据结构的应用(数组、链表、栈、哈希表)
- 都需要某种算法思想(双指针、滑动窗口、动态规划、回溯)
- 都有明确的边界条件需要处理
- 大部分都有时间复杂度优于暴力解法的优化方案
6.2 面试中的解题步骤
在实际面试中,我建议按照以下步骤解题:
- 明确问题:复述题目要求,确认理解正确
- 举例说明:用具体例子演示输入输出
- 暴力解法:先给出最直观的解法
- 优化思路:分析瓶颈,提出优化方向
- 代码实现:编写清晰可读的代码
- 测试用例:用边缘案例验证代码
6.3 常见错误与避免方法
根据我的经验,面试中常见的错误包括:
- 没有处理边界条件(空输入、极值等)
- 变量命名混乱,代码可读性差
- 没有考虑时间/空间复杂度
- 过度优化导致代码难以理解
提示:在面试前,至少要将这20道题手写实现3遍,确保能在白板上无bug写出。我通常会创建一个表格记录每道题的思路要点和易错点。
6.4 后续学习建议
掌握这20题后,建议:
- 尝试每道题的变种(如Three Sum的变种)
- 学习更高级的数据结构(堆、Trie树、并查集)
- 练习系统设计题目
- 参加模拟面试检验学习成果
我在准备面试时,会每天解决2-3道新题,同时复习5道已掌握的题目。这种交替学习的方式效果很好,既能扩展知识面,又能巩固基础。记住,刷题质量比数量更重要,理解每道题背后的思想才是关键。
code复制