1. 为什么数组面试题如此重要?
数组作为计算机科学中最基础的数据结构之一,几乎出现在所有技术岗位的面试中。我在担任面试官的这些年里,发现90%的候选人都会在数组问题上栽跟头——不是因为他们不懂数组,而是没有掌握数组问题的解题套路。
数组问题之所以高频出现,主要有三个原因:首先,它能很好地考察候选人对基础数据结构的理解;其次,数组问题可以衍生出各种算法考察点;最后,数组操作在实际工程中无处不在。今天我就带大家深度解析7道必刷的数组面试题,这些题目全部来自一线大厂的真实面试题库。
2. 7道必刷题目详解
2.1 两数之和(Two Sum)
这是LeetCode上的第一题,也是出现频率最高的数组问题。题目要求:给定一个整数数组nums和一个目标值target,找出数组中和为目标值的两个整数,并返回它们的下标。
暴力解法:双重循环遍历所有组合,时间复杂度O(n²)。这是大多数人的第一反应,但面试官期待更好的解法。
优化解法:使用哈希表存储已遍历元素,只需一次遍历:
python复制def twoSum(nums, target):
hashmap = {}
for i, num in enumerate(nums):
complement = target - num
if complement in hashmap:
return [hashmap[complement], i]
hashmap[num] = i
return []
注意:当数组中有重复元素时,哈希表解法仍然有效,因为我们会先检查匹配再更新哈希表。
2.2 旋转数组(Rotate Array)
题目要求将数组向右旋转k个位置,要求空间复杂度O(1)。例如:[1,2,3,4,5]旋转3次变为[3,4,5,1,2]。
三次反转法是最优解:
- 反转整个数组
- 反转前k个元素
- 反转剩余元素
python复制def rotate(nums, k):
k %= len(nums)
nums.reverse()
nums[:k] = reversed(nums[:k])
nums[k:] = reversed(nums[k:])
实操心得:k可能大于数组长度,所以要先取模。这个解法在O(1)空间下完成操作,是面试官最想看到的。
2.3 移动零(Move Zeroes)
题目要求将所有0移动到数组末尾,同时保持非零元素的相对顺序。必须在原数组上操作,不能拷贝额外的数组。
双指针法是标准解法:
python复制def moveZeroes(nums):
slow = 0
for fast in range(len(nums)):
if nums[fast] != 0:
nums[slow], nums[fast] = nums[fast], nums[slow]
slow += 1
这个解法中,slow指针始终指向下一个非零元素应该存放的位置,fast指针遍历整个数组。时间复杂度O(n),空间复杂度O(1)。
2.4 盛最多水的容器(Container With Most Water)
给定一个数组表示容器的高度,找出两条线使得它们与x轴共同构成的容器可以容纳最多的水。
双指针法从两端向中间移动:
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
关键点:每次移动较矮的那一端,因为容器的容量由较矮的一端决定。这个解法的时间复杂度是O(n)。
2.5 三数之和(3Sum)
找出数组中所有不重复的三元组,使得它们的和为0。这是两数之和的进阶版,也是面试中的常客。
排序+双指针解法:
python复制def threeSum(nums):
nums.sort()
res = []
for i in range(len(nums)-2):
if i > 0 and nums[i] == nums[i-1]:
continue
left, right = i+1, len(nums)-1
while left < right:
s = nums[i] + nums[left] + nums[right]
if s < 0:
left += 1
elif s > 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
避坑指南:必须跳过重复元素以避免重复解。时间复杂度O(n²),排序的时间复杂度是O(nlogn)。
2.6 最长连续序列(Longest Consecutive Sequence)
给定一个未排序的整数数组,找出数字连续的最长序列的长度。要求时间复杂度O(n)。
哈希集合解法:
python复制def longestConsecutive(nums):
num_set = set(nums)
max_streak = 0
for num in num_set:
if num - 1 not in num_set:
current_num = num
current_streak = 1
while current_num + 1 in num_set:
current_num += 1
current_streak += 1
max_streak = max(max_streak, current_streak)
return max_streak
技巧:只有当num是序列的起点时(即num-1不在集合中)才开始计数。这样确保每个序列只被处理一次,达到O(n)时间复杂度。
2.7 合并区间(Merge Intervals)
给出一个区间的集合,合并所有重叠的区间。这是处理时间区间、日程安排等实际问题的简化模型。
排序+合并解法:
python复制def merge(intervals):
intervals.sort(key=lambda x: x[0])
merged = []
for interval in intervals:
if not merged or merged[-1][1] < interval[0]:
merged.append(interval)
else:
merged[-1][1] = max(merged[-1][1], interval[1])
return merged
注意事项:必须先按区间起点排序。合并时只需要比较当前区间与结果列表中最后一个区间的关系即可。
3. 数组问题解题方法论
3.1 常见解题模式总结
通过以上7道题目,我们可以总结出数组问题的几种常见解题模式:
| 模式 | 适用场景 | 时间复杂度 | 典型题目 |
|---|---|---|---|
| 双指针 | 有序数组、滑动窗口 | O(n) | 两数之和、盛水容器 |
| 哈希表 | 快速查找、去重 | O(n) | 两数之和、最长连续序列 |
| 排序预处理 | 需要有序性的问题 | O(nlogn) | 三数之和、合并区间 |
| 原地操作 | 空间复杂度要求O(1) | O(n) | 移动零、旋转数组 |
3.2 面试中的常见误区
根据我的面试经验,候选人在数组问题上常犯以下错误:
- 忽视边界条件:空数组、单元素数组、全零数组等特殊情况
- 过度使用额外空间:面试官常要求原地操作时仍申请新数组
- 忽略时间复杂度优化:满足于暴力解法而不思考更优解
- 处理重复元素不当:特别是在三数之和这类题目中
- 指针移动逻辑错误:双指针问题中指针移动条件不清晰
3.3 实战演练建议
为了真正掌握这些题目,我建议:
- 先自己尝试:不要直接看答案,至少思考15分钟
- 手写代码:面试中常常需要手写,提前适应这种形式
- 测试边界用例:空数组、极端值、重复元素等情况
- 复杂度分析:能清晰解释自己解法的时间空间复杂度
- 多种解法对比:思考是否有更优解,不同解法的适用场景
4. 高频变种问题解析
4.1 两数之和变种
变种1:数组已排序
此时可以使用双指针法,空间复杂度降至O(1)
变种2:设计数据结构支持频繁查询
可以预处理数组建立哈希表,后续查询只需O(1)时间
4.2 三数之和变种
最接近的三数之和:找到和最接近目标值的三元组
解法类似,但在移动指针时需要记录最接近的和
四数之和:在三数之和基础上再加一层循环
时间复杂度升至O(n³),但思路相同
4.3 合并区间变种
插入新区间:在已有区间集合中插入新区间并合并
可以先插入再合并,或者直接找到插入位置边插入边合并
区间交集:求两个区间列表的交集
使用双指针法依次比较区间,时间复杂度O(m+n)
5. 面试技巧与注意事项
5.1 回答策略
- 先确认题意:询问输入范围、边界条件、输出要求
- 举例说明:用具体例子解释自己的思路
- 先讲暴力解:再逐步优化,展示思考过程
- 复杂度分析:主动分析时间空间复杂度
- 测试用例:写完代码后主动验证几个测试用例
5.2 代码风格建议
- 变量命名:使用有意义的变量名而非简单i,j
- 注释关键步骤:特别是复杂的指针操作
- 函数拆分:复杂问题拆分成多个小函数
- 异常处理:考虑输入不合法的情况
- 代码对齐:良好的缩进和格式提升可读性
我在面试中见过太多候选人因为数组问题表现不佳而错失机会。实际上,只要掌握这7道核心题目及其变种,数组类问题就能成为你的得分项。建议至少将每道题目手写3遍,直到能闭眼写出最优解为止。