数组缺一个数时,优先用求和法(0到n理论和减实际和),其次哈希集合、原地交换或异或法;关键先确认范围是否连续、起始值及能否修改数组。

数组元素范围已知且连续时,用求和公式最稳
如果数组是 0 到 n 的整数缺一个(比如长度为 n 的数组本该含 0~n 共 n+1 个数),直接用数学法:理论总和减去实际总和就是缺失值。它不改原数组、时间 O(n)、空间 O(1),且无溢出风险(用 long long 更保险)。
常见错误是误用 n*(n+1)/2 却没确认起始是否为 0 或范围是否真连续——比如数组是 [1,2,4,5] 缺 3,但若你按 0~5 算就错了。
实操建议:
- 先确认题目隐含条件:是否从
0或1开始?是否严格连续? - 计算理论和时用
static_cast<long long>(n) * (n + 1) / 2</long>避免int溢出 - 遍历累加时用相同类型,别让
sum是int而理论和是long long
int missingNumber(const vector<int>& nums) {
int n = nums.size();
long long expected = static_cast<long long>(n) * (n + 1) / 2;
long long actual = 0;
for (int x : nums) actual += x;
return expected - actual;
}</long></int>
数组含重复或范围未知时,必须用哈希集合
一旦数组可能有重复、负数、或缺失多个数字(比如 [3,0,1] 缺 2,但你也得知道上限是 3),求和法失效。此时得明确「缺失」的定义域——通常题目会说“在 0 到 n 中缺一个”,那你就建一个 unordered_set 存所有出现的数,再从 0 到 n 扫一遍。
性能上,哈希查找均摊 O(1),总时间 O(n),但空间升到 O(n);有人试图用 vector<bool></bool> 替代,但要注意下标越界——比如 n=3 时你要查 0~3,vector 至少要开 4 位。
实操建议:
- 别用
set(红黑树),unordered_set足够且更快 - 循环检查范围必须是
[0, nums.size()],不是[0, nums.size()-1] - 插入前不用判重,
unordered_set::insert本身幂等
int missingNumber(const vector<int>& nums) {
unordered_set<int> seen(nums.begin(), nums.end());
int n = nums.size();
for (int i = 0; i <h3>原地交换法适合“数值即下标”类题目,但易写错边界</h3>
<p>当题目明确说数组由 <code>0</code> 到 <code>n-1</code> 的整数构成、仅缺一个,且允许修改原数组时,可把每个数放到对应下标位置(即让 <code>nums[i] == i</code>),最后扫一遍找第一个不满足的位置。这是真正的 <code>O(1)</code> 空间解法,但逻辑稍绕,容易陷入死循环或越界。</p><div class="aritcle_card flexRow artxards">
<div class="artcardd flexRow">
<a class="aritcle_card_img" rel="nofollow" href="/xiazai/gongju/2823" title="C++14"><img
src="https://img.php.cn/upload/manual/001/431/639/6ac8b33c327c4749.png" alt="C++14" onerror="this.onerror='';this.src='/static/lhimages/moren/morentu.png'" ></a>
<div class="aritcle_card_info flexColumn">
<a rel="nofollow" href="/xiazai/gongju/2823" title="C++14" class="overflowclass">C++14</a>
<p class="overflowclass">C++14 对 C++11 的修正与增强版本,适合旧系统维护和较老工具链兼容。</p>
</div>
<a rel="nofollow" href="/xiazai/gongju/2823" title="C++14" class="aritcle_card_btn flexRow flexcenter"><b></b><span>下载</span>
</a>
</div>
</div>
<p>关键陷阱是交换条件判断:必须确保 <code>nums[i] != i</code> 且 <code>nums[i] 且 <code>nums[nums[i]] != nums[i]</code>,否则可能把已归位的数又换走,或对 <code>n</code>(超出下标)做访问。</code></p>
<p>实操建议:</p>
<ul>
<li>内层 <code>while</code> 循环必须检查 <code>nums[i] ,否则 <code>nums[nums[i]]</code> 会越界</code>
</li>
<li>交换后不要立即 <code>i++</code>,因为换进来的新值未必就位,得继续处理</li>
<li>最终返回 <code>i</code>,不是 <code>nums[i]</code>——因为此时 <code>nums[i] != i</code>,缺的就是 <code>i</code>
</li>
</ul>
<pre class="brush:php;toolbar:false;">int missingNumber(vector<int>& nums) {
int n = nums.size();
for (int i = 0; i <h3>位运算异或法简洁但适用场景极窄</h3>
<p>利用 <code>a ^ a == 0</code> 和 <code>a ^ 0 == a</code>,把 <code>0~n</code> 所有数和数组所有数一起异或,成对出现的抵消,只剩缺失数。它和求和法一样快且省空间,但只适用于“缺且仅缺一个、范围确定、无重复”的情况。</p>
<p>最容易错的是异或范围:数组长 <code>n</code>,完整集是 <code>0</code> 到 <code>n</code>(共 <code>n+1</code> 个数),所以循环要跑 <code>i = 0</code> 到 <code>n</code>,同时把 <code>nums[i]</code> 异或进去——但 <code>nums</code> 只有 <code>n</code> 个元素,最后一个 <code>i == n</code> 时不能访问 <code>nums[n]</code>。</p>
<p>实操建议:</p>
<ul>
<li>先初始化 <code>ans = n</code>(即把最大数带上),再对 <code>i = 0</code> 到 <code>n-1</code> 异或 <code>i</code> 和 <code>nums[i]</code>
</li>
<li>别写成两个分开的循环,容易漏一个数</li>
<li>异或顺序无关,但变量类型保持 <code>int</code> 即可,无需扩大</li>
</ul>
<pre class="brush:php;toolbar:false;">int missingNumber(const vector<int nums int ans="nums.size();" n for i="0;" nums.size return><p>真正难的不是选哪种方法,而是读题时立刻判断清楚:范围是否闭合?能否修改数组?是否允许多解?这些定性判断比写代码还容易出错。</p></int>C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!










