LeetCode Hot 100 | 技巧(C++ 题解)
Hot100 技巧(官方顺序):
136 / 169 / 75 / 31 / 287。
目录
前言
"技巧"专题汇集了利用位运算、摩尔投票、双指针等非常规方法解决问题的经典题目。本篇涵盖只出现一次的数字、多数元素、颜色分类、下一个排列和寻找重复数五道题(268、448暂无C++ AC提交,跳过)。
1. 136. 只出现一次的数字 🟢
题目描述
给你一个 非空 整数数组 nums,除了某个元素只出现 一次 以外,其余每个元素均出现 两次。找出那个只出现了一次的元素。
你必须设计并实现线性时间复杂度的算法来解决此问题,且该算法只使用常量额外空间。
示例 1:
输入:nums = [2,2,1]
输出:1
示例 2:
输入:nums = [4,1,2,1,2]
输出:4
约束: 1 <= nums.length <= 3 * 10^4, -3 * 10^4 <= nums[i] <= 3 * 10^4
图解

解题思路
异或性质:a ^ a = 0,a ^ 0 = a。将所有元素异或,成对的数字相消,剩下只出现一次的数字。
以 nums = [4,1,2,1,2] 为例:
| 步骤 | x | ans |
|---|---|---|
| 初始 | - | 0 |
| x=4 | 4 | 0^4=4 |
| x=1 | 1 | 4^1=5 |
| x=2 | 2 | 5^2=7 |
| x=1 | 1 | 7^1=6 |
| x=2 | 2 | 6^2=4 ✓ |
复杂度分析
- 时间: O(n)
- 空间: O(1)
C++ 代码
class Solution {
public:
int singleNumber(vector<int>& nums) {
int ans = 0;
for (int &x : nums)
ans ^=x;
return ans;
}
};
2. 169. 多数元素 🟢
题目描述
给定一个大小为 n 的数组 nums,返回其中的多数元素。多数元素是指在数组中出现次数 大于 ⌊ n/2 ⌋ 的元素。
你可以假设数组是非空的,并且给定的数组总是存在多数元素。
示例 1:
输入:nums = [3,2,3]
输出:3
示例 2:
输入:nums = [2,2,1,1,1,2,2]
输出:2
约束: n == nums.length, 1 <= n <= 5 * 10^4, -10^9 <= nums[i] <= 10^9
图解

解题思路
摩尔投票:维护候选人 ans 和计数 count。若 count==0 则换候选人,若当前元素与候选人相同则 +1,否则 -1。超过半数的元素必然是最终候选人。
以 nums = [2,2,1,1,1,2,2] 为例:
| x | count | ans | 操作 |
|---|---|---|---|
| 2 | 0→1 | 2 | count=0, ans=2, count=1 |
| 2 | 1→2 | 2 | 相同, count=2 |
| 1 | 2→1 | 2 | 不同, count=1 |
| 1 | 1→0 | 2 | 不同, count=0 |
| 1 | 0→1 | 1 | count=0, ans=1, count=1 |
| 2 | 1→0 | 1 | 不同, count=0 |
| 2 | 0→1 | 2 | count=0, ans=2, count=1 |
返回 ans=2 ✓
复杂度分析
- 时间: O(n)
- 空间: O(1)
C++ 代码
class Solution {
public:
int majorityElement(vector<int>& nums) {
int count = 0;
int ans = 0;
for (int &x : nums) {
if (count == 0) {
ans = x;
count++;
} else {
if (ans != x)
count--;
else
count++;
}
}
return ans;
}
};
3. 75. 颜色分类 🟡
题目描述
给定一个包含红色、白色和蓝色、共 n 个元素的数组 nums,原地 对它们进行排序,使得相同颜色的元素相邻,并按照红色、白色、蓝色顺序排列。
我们使用整数 0、1 和 2 分别表示红色、白色和蓝色。必须在不使用库内置的 sort 函数的情况下解决这个问题。
示例 1:
输入:nums = [2,0,2,1,1,0]
输出:[0,0,1,1,2,2]
示例 2:
输入:nums = [2,0,1]
输出:[0,1,2]
约束: n == nums.length, 1 <= n <= 300, nums[i] 为 0、1 或 2
图解

解题思路
荷兰国旗问题(三路快排):维护三个指针 left(0的右边界)、right(2的左边界)、i(当前遍历):
nums[i]==0:与left互换,left++, i++nums[i]==2:与right互换,right–(i不动,需再检查)nums[i]==1:i++
以 nums = [2,0,2,1,1,0] 为例:
| i | left | right | nums[i] | 操作 | 数组 |
|---|---|---|---|---|---|
| 0 | 0 | 5 | 2 | 与right互换,right– | [0,0,2,1,1,2] |
| 0 | 0 | 4 | 0 | 与left互换,left++,i++ | [0,0,2,1,1,2] |
| 1 | 1 | 4 | 0 | 与left互换 | [0,0,2,1,1,2] |
| … | [0,0,1,1,2,2] |
复杂度分析
- 时间: O(n)
- 空间: O(1)
C++ 代码
class Solution {
public:
void sortColors(vector<int>& nums) {
int left = 0;
int right = nums.size() - 1;
int i = 0;
while (i <= right) {
if (nums[i] == 0) {
swap(nums[i], nums[left]);
left++;
i++;
} else if (nums[i] == 2) {
swap(nums[i], nums[right]);
right--;
} else {
i++;
}
}
}
};
4. 31. 下一个排列 🟡
题目描述
整数数组的一个 排列 就是将其所有成员以序列或线性顺序排列。
下一个排列 是指其整数的下一个字典序更大的排列。如果不存在下一个更大的排列,则将数字重新排列成最小的排列(即升序排列)。
必须 原地 修改,只允许使用额外常数空间。
示例 1:
输入:nums = [1,2,3]
输出:[1,3,2]
示例 2:
输入:nums = [3,2,1]
输出:[1,2,3]
示例 3:
输入:nums = [1,1,5]
输出:[1,5,1]
约束: 1 <= nums.length <= 100, 0 <= nums[i] <= 100
图解

解题思路
三步走:
- 从右找第一个下降点
pos(nums[pos] < nums[pos+1]) - 从右找第一个大于
nums[pos]的数,与pos交换 - 将
pos+1之后的部分翻转(由降序变升序)
若不存在下降点(整体降序),pos=-1,直接翻转全部。
以 nums = [1,2,3] 为例:
| 步骤 | 操作 | 数组 |
|---|---|---|
| 1 | 从右找下降点: pos=1(nums[1]=2 < nums[2]=3) | [1,2,3] |
| 2 | 从右找>2的数: i=2(3>2), swap(nums[2],nums[1]) | [1,3,2] |
| 3 | 翻转pos+1=2之后: 无需翻转 | [1,3,2] ✓ |
复杂度分析
- 时间: O(n)
- 空间: O(1)
C++ 代码
class Solution {
public:
void nextPermutation(vector<int>& nums) {
int n = nums.size();
int pos = -1;
for (int i = n - 1; i >= 1; i--) {
if (nums[i - 1] < nums[i]) {
pos = i - 1;
break;
}
}
if (pos != -1) {
for (int i = n - 1; i > pos; i--) {
if (nums[i] > nums[pos]) {
swap(nums[i], nums[pos]);
break;
}
}
}
std::cout<< pos << std::endl;
reverse(nums.begin() + pos + 1, nums.end());
}
};
5. 287. 寻找重复数 🟡
题目描述
给定一个包含 n + 1 个整数的数组 nums,其数字都在 [1, n] 范围内(包括 1 和 n),可知至少存在一个重复的整数。
假设 nums 只有 一个重复的整数,返回 这个重复的数。
你设计的解决方案必须 不修改 数组 nums 且只用常量级 O(1) 的额外空间。
示例 1:
输入:nums = [1,3,4,2,2]
输出:2
示例 2:
输入:nums = [3,1,3,4,2]
输出:3
约束: 1 <= n <= 10^5, nums.length == n + 1, 1 <= nums[i] <= n
图解

解题思路
Floyd 判圈算法(龟兔赛跑):将数组视为链表(i -> nums[i]),重复数即为环的入口。快慢指针相遇后,用新指针从头出发,与慢指针同速走,再次相遇即环入口。
以 nums = [1,3,4,2,2] 为例:
| 步骤 | slow | fast | 路径 |
|---|---|---|---|
| 初始 | 0 | 0 | |
| 1 | nums[0]=1 | nums[nums[0]]=3 | |
| 2 | nums[1]=3 | nums[nums[3]]=2 | |
| 3 | nums[3]=2 | nums[nums[2]]=2 | slow==fast=2, 相遇 |
| head=0 | slow=2 | head=0 | |
| 4 | nums[2]=4 | nums[0]=1 | |
| 5 | nums[4]=2 | nums[1]=3 | |
| 6 | nums[2]=4→… | 实际: head走1→3→2,slow走2→4→2,在2处相遇,返回2 ✓ |
复杂度分析
- 时间: O(n)
- 空间: O(1)
C++ 代码
class Solution {
public:
int findDuplicate(vector<int>& nums) {
int fast = 0;
int slow = 0;
while (true) {
slow = nums[slow];
fast = nums[nums[fast]];
if (slow == fast)
break;
}
int head = 0;
while (head != slow) {
slow = nums[slow];
head = nums[head];
}
return slow;
}
};
⚠️ 以下题目未找到 C++ AC 提交记录,跳过:
- 丢失的数字
- 找到所有数组中消失的数字
总结
| 题号 | 题目 | 难度 | 核心技巧 | 时间复杂度 |
|---|---|---|---|---|
| 136 | 只出现一次的数字 | 🟢简单 | 全员异或,成对消零 | O(n) |
| 169 | 多数元素 | 🟢简单 | 摩尔投票,超半数必胜 | O(n) |
| 75 | 颜色分类 | 🟡中等 | 荷兰国旗三路快排 | O(n) |
| 31 | 下一个排列 | 🟡中等 | 找下降点→交换→翻转后缀 | O(n) |
| 287 | 寻找重复数 | 🟡中等 | Floyd判圈,数组模拟链表 | O(n) |
| 268 | 丢失的数字 | 🟢简单 | 暂无C++ AC提交 | - |
| 448 | 消失的数字 | 🟢简单 | 暂无C++ AC提交 | - |
&spm=1001.2101.3001.5002&articleId=159908409&d=1&t=3&u=fbffdf3f83df4d51803c2a6b35f56f54)
1231

被折叠的 条评论
为什么被折叠?



