std::find不能直接找出所有消失数字,因为它只返回首个匹配项迭代器,而找消失数字本质是求[1,n]与数组元素集合的补集;需用布尔数组标记或原地负号标记法实现o(n)时间、o(1)额外空间解法。

为什么 std::find 不能直接找出所有消失的数字
因为 std::find 只返回第一个匹配项的迭代器,找不到就返回 end(),它不负责“枚举缺失值”。数组里缺哪些数字,本质是集合补集问题:给定范围 [1, n],现有数组含部分数,要找没出现过的那些——得自己构造全集或标记状态。
用布尔数组标记法最直观且不易越界
假设输入数组 nums 长度为 n,题目隐含条件是所有元素都在 [1, n] 范围内(LeetCode 448 经典题)。这时开一个长度为 n+1 的 std::vector<bool></bool>(下标 0 不用),遍历 nums 把每个数对应位置设为 true:
std::vector<int> findDisappearedNumbers(std::vector<int>& nums) {
int n = nums.size();
std::vector<bool> seen(n + 1, false); // seen[1] ~ seen[n]
for (int x : nums) seen[x] = true;
<pre class="brush:php;toolbar:false;">std::vector<int> res;
for (int i = 1; i <p>}</p></int>
注意点:
-
seen大小必须是n+1,否则访问seen[n]会越界 - 循环从
i = 1开始,不是0——题目要的是1到n中消失的数 - 不用
std::set或std::unordered_set,避免哈希开销和内存碎片
空间 O(1) 解法要用原数组做标记,但容易改错索引
核心技巧:把 nums[i] 的绝对值减 1 当作下标,将对应位置的数改成负数,表示“这个下标 + 1 的数已存在”。最后再扫一遍,仍为正的那些位置 + 1 就是答案。
关键陷阱:
- 取绝对值必须在每次读取后立刻做:
int idx = abs(nums[i]) - 1;,否则遇到已变负的数会算错下标 - 标记时只对仍为正的数取反:
if (nums[idx] > 0) nums[idx] = -nums[idx];,否则可能把负号又翻回去 - 第二次遍历时,判断条件是
nums[i] > 0,不是!= 0——因为 0 不会出现,但若误写成== 0就全漏了
如果数组含 0、负数或超出范围的数怎么办
题目原始约束通常排除这些情况,但实际工程中得先校验。此时布尔数组法更鲁棒:
- 先遍历一次,过滤出
[1, n]内的有效数,再建seen - 若需支持任意整数,就得用
std::unordered_set存已见数字,再遍历1到n查是否在集合中——时间 O(n),空间 O(n) - 别试图对非法数做原地标记,
abs(-100)可能远超数组边界,直接崩
真正麻烦的从来不是算法逻辑,而是没看清输入范围就硬套模板。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











