前言
今天复盘两道面试高频的数组题,一道是利用摩尔投票算法的经典题「多数元素」 ,另一道是荷兰国旗问题的典型应用「颜色分类」。这两道题分别代表了 "特殊条件下的最优解" 和 "原地排序的双指针技巧",是数组处理中非常实用的算法模板。
一、169. 多数元素(简单)
题目描述
给定一个大小为 n 的数组,找到其中的多数元素。多数元素是指在数组中出现次数大于 ⌊n/2⌋ 的元素。 你可以假设数组是非空的,并且给定的数组总是存在多数元素。
核心思路:摩尔投票算法
摩尔投票算法的核心思想是 **"正负抵消"**:因为多数元素的出现次数超过一半,所以它在和其他元素两两抵消后,最终一定会剩下。
- 维护一个候选元素
candidate和它的票数count - 遍历数组:
- 若
count == 0,将当前元素设为新的候选 - 若当前元素等于候选,
count++;否则count--
- 若
- 遍历结束后,
candidate就是多数元素
完整代码(Java)
java
运行
class Solution {
public int majorityElement(int[] nums) {
int candidate = nums[0];
int count = 1;
for (int i = 1; i < nums.length; i++) {
if (count == 0) {
candidate = nums[i];
count = 1;
} else if (nums[i] == candidate) {
count++;
} else {
count--;
}
}
return candidate;
}
}
复杂度分析
- 时间复杂度:O (n),仅需一次遍历数组
- 空间复杂度:O (1),仅使用常数额外空间
拓展思考
摩尔投票算法还可以拓展到 "找出数组中出现次数大于 n/3 的元素",最多有两个候选元素,需要两轮遍历:第一轮投票找出候选,第二轮验证是否满足条件。
二、75. 颜色分类(中等)
题目描述
给定一个包含红色、白色和蓝色,一共 n 个元素的数组,原地对它们进行排序,使得相同颜色的元素相邻,并按照红色、白色、蓝色顺序排列。 我们使用整数 0、1 和 2 分别表示红色、白色和蓝色。 注意:不能使用库函数来排序,必须原地解决。
核心思路:双指针(荷兰国旗问题)
这道题的最优解法是三指针原地交换,核心思想是:
- 定义
p0指向0元素的右边界,p2指向2元素的左边界,curr指向当前遍历元素 - 遍历数组:
- 若
nums[curr] == 0,将其与nums[p0]交换,p0++、curr++ - 若
nums[curr] == 2,将其与nums[p2]交换,p2--(此时curr不前进,因为交换过来的元素还需要判断) - 若
nums[curr] == 1,直接curr++
- 若
完整代码(Java)
java
运行
class Solution {
public void sortColors(int[] nums) {
int p0 = 0, curr = 0;
int p2 = nums.length - 1;
while (curr <= p2) {
if (nums[curr] == 0) {
swap(nums, curr, p0);
p0++;
curr++;
} else if (nums[curr] == 2) {
swap(nums, curr, p2);
p2--;
} else {
curr++;
}
}
}
private void swap(int[] nums, int i, int j) {
int temp = nums[i];
nums[i] = nums[j];
nums[j] = temp;
}
}
复杂度分析
- 时间复杂度:O (n),仅需一次遍历数组
- 空间复杂度:O (1),原地交换,未使用额外空间
拓展思考
荷兰国旗问题可以拓展到更多颜色的排序场景,核心思想都是通过指针将元素分区,一次遍历完成排序,避免了多次遍历或额外空间的开销。
两道题对比总结
表格
| 题目 | 核心思想 | 时间复杂度 | 空间复杂度 | 关键考点 |
|---|---|---|---|---|
| 多数元素 | 摩尔投票算法(正负抵消) | O(n) | O(1) | 多数元素的定义、投票过程 |
| 颜色分类 | 三指针原地分区(荷兰国旗问题) | O(n) | O(1) | 双指针技巧、原地交换 |