目录
一
[LeetCode - 消失的数字](#LeetCode - 消失的数字)
二
[LeetCode - 轮转数组](#LeetCode - 轮转数组)
1,LeetCode - 消失的数字
数组 nums 包含从 0 到 n 的所有整数,但其中缺了一个,请找出那个缺失的整数。要求 O(n) 时间完成。
解题思路
暴力思路(不推荐)
排序后遍历找断点,时间复杂度 O(n log n),不符合要求。
最优解:异或(XOR)
核心观察 :把数组里的数和
0~n全部异或在一起,相同的数两两抵消,最后剩下的就是缺失的数。
nums = [3, 0, 1],n = 3,缺 2 x = 3^0^1 ^ 0^1^2^3 = (0^0)^(1^1)^(3^3)^2 = 0^0^0^2 = 2 ✓
cppint missingNumber(int* nums, int numsSize) { int x = 0; for (int i = 0; i < numsSize; i++) x ^= nums[i]; // 异或数组所有元素 for (int j = 0; j <= numsSize; j++) x ^= j; // 异或 0~n return x; // 剩下的就是缺失值 }复杂度:时间 O(n),空间 O(1)。
异或原理详解
异或的本质是逐位做加法 mod 2:9
性质 公式 说明 归零律 a ^ a = 0相同数异或为 0 恒等律 0 ^ a = a0 与任何数异或为本身 交换律 a ^ b = b ^ a加法 mod 2 满足交换律 结合律 (a^b)^c = a^(b^c)加法 mod 2 满足结合律 交换律和结合律成立,意味着两个循环的异或顺序可以任意调换,相同数字必然在某处相遇并抵消,只有出现奇数次的数字会保留下来。
mod 2 就是除以2取余数,结果只有 0 或 1。
0 mod 2 = 0 1 mod 2 = 1 2 mod 2 = 0 3 mod 2 = 1联系到异或,举个例子,看某一个二进制位:
0 ^ 0 = 0 → (0+0) mod 2 = 0 ✓ 0 ^ 1 = 1 → (0+1) mod 2 = 1 ✓ 1 ^ 0 = 1 → (1+0) mod 2 = 1 ✓ 1 ^ 1 = 0 → (1+1) mod 2 = 0 ✓ (2除以2余0,不进位,直接归零)异或和普通加法唯一的区别就是 1+1 不进位,直接变成 0,这就是 mod 2 的含义。
所以说"异或 = 加法 mod 2",每个二进制位独立这样算,整个整数就异或完了。
扩展:单身狗问题
同样的技巧可以解决"数组中只出现一次的数字":把所有元素全部异或,成对的数归零,剩下的就是答案。这类问题统称单身狗问题,XOR 是标准解法。
总结
| 题目 | 核心思想 | 时间 | 空间 |
|---|---|---|---|
| 消失的数字 | 异或抵消,相同归零 | O(n) | O(1) |
2,LeetCode - 轮转数组
给定整数数组 nums ,将数组元素向右轮转 **k**个位置。
解题思路
暴力:逐个移位(超时)
每次把最后一个元素移到最前面,执行 k 次,时间复杂度 O(n×k),最坏 O(n²),数据量大时超时。
c
cpp// O(n²) 超时写法,仅作对比 void rotate_slow(int* nums, int numsSize, int k) { k %= numsSize; while (k--) { int last = nums[numsSize - 1]; for (int i = numsSize - 2; i >= 0; i--) nums[i + 1] = nums[i]; nums[0] = last; } }
最优解:三次反转
规律发现:
原数组: 1 2 3 4 5 6 7,k = 3
目标结果:5 6 7 1 2 3 4
步骤:
① 反转前 n-k 个:[1 2 3 4] → [4 3 2 1] → 4 3 2 1 | 5 6 7
② 反转后 k 个: [5 6 7] → [7 6 5] → 4 3 2 1 | 7 6 5
③ 整体反转: 【4 3 2 1 7 6 5】 → [ 5 6 7 1 2 3 4 ]
cppvoid reverse(int* nums, int left, int right) { while (left < right) { int tmp = nums[left]; nums[left] = nums[right]; nums[right] = tmp; left++; right--; } } void rotate(int* nums, int numsSize, int k) { k %= numsSize; // k 可能大于 n,取模防越界 int n = numsSize; reverse(nums, 0, n-k-1); // ① 反转前半段 [0, n-k-1] reverse(nums, n-k, n-1); // ② 反转后半段 [n-k, n-1] reverse(nums, 0, n-1); // ③ 整体反转 }复杂度:时间 O(n),空间 O(1)。
关键细节
k %= numsSize不能省:若 k = 7、n = 7,轮转一整圈等于没转,不取模会导致下标越界或逻辑错误。三段区间划分:
步骤 区间 说明 ① [0, n-k-1]不需要移动的前半段 ② [n-k, n-1]需要移到前面的后 k 个 ③ [0, n-1]整体反转得到最终结果
总结
| 题目 | 核心思想 | 时间 | 空间 |
|---|---|---|---|
| 轮转数组 | 三次反转,区间划分准确 | O(n) | O(1) |
是用简单操作的组合达到线性时间,是位运算和双指针的经典入门题,面试高频出现。
