前言
今天复盘两道 LeetCode 上的经典 "简单题",它们看起来容易,但解法里藏着非常巧妙的算法思想:一道是位运算的经典应用 ,另一道是摩尔投票算法的入门模板。这两道题不仅是面试高频考点,更是理解算法优化思路的绝佳案例。
一、136. 只出现一次的数字(简单)
题目描述
给定一个非空整数数组,除了某个元素只出现一次以外,其余每个元素均出现两次。找出那个只出现了一次的元素。 要求:线性时间复杂度、常数额外空间。
核心思路:异或运算的妙用
这道题的 "黑科技" 解法,核心是利用异或运算的三个性质:
a ^ a = 0:相同的数异或结果为 0a ^ 0 = a:任何数和 0 异或,结果不变a ^ b ^ a = b:异或运算满足交换律和结合律
所以,我们只需要把数组里的所有数从头到尾异或一遍,最终的结果就是那个只出现一次的数字 ------ 所有成对出现的数都会异或为 0,最后剩下的就是那个单独的数。
完整代码(Java)
java
运行
class Solution {
public int singleNumber(int[] nums) {
int res = 0;
for (int num : nums) {
res ^= num;
}
return res;
}
}
复杂度分析
- 时间复杂度:O (n),只遍历一次数组
- 空间复杂度:O (1),只用了一个变量存结果
拓展思考
如果题目改成 "其余元素出现三次,只有一个元素出现一次",就不能直接用异或了,需要用位统计的方法,但核心思路依然是利用位运算的特性。
二、169. 多数元素(简单)
题目描述
给定一个大小为 n 的数组,找到其中的多数元素。多数元素是指在数组中出现次数 大于 ⌊n/2⌋ 的元素。 要求:线性时间复杂度、O (1) 空间复杂度。
核心思路:摩尔投票算法
这道题的最优解是摩尔投票算法,它的核心思想可以理解为 "正负抵消":
- 维护一个候选元素
candidate和它的票数count - 遍历数组:
- 如果
count == 0,把当前元素设为新的候选 - 如果当前元素等于候选,
count++;否则count--
- 如果
- 因为多数元素的出现次数超过一半,它在和其他元素 "抵消" 后,最终一定会成为剩下的候选。
完整代码(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 的元素",最多有两个候选元素,需要两轮遍历:第一轮投票找出候选,第二轮验证是否真的满足条件。
两道题对比总结
表格
| 题目 | 核心思想 | 时间复杂度 | 空间复杂度 | 关键考点 |
|---|---|---|---|---|
| 只出现一次的数字 | 异或运算的性质 | O(n) | O(1) | 位运算、交换律结合律 |
| 多数元素 | 摩尔投票算法(正负抵消) | O(n) | O(1) | 多数元素的定义、投票过程 |
这两道题都是 "看起来简单,解法很巧妙" 的典型,掌握它们的思路,不仅能解决原题,更能帮你理解算法优化的核心思想 ------ 利用问题的特性,用更高效的方式实现目标。