【数据结构】消失的数字+ 轮转数组:踩坑详解

目录

[LeetCode - 消失的数字](#LeetCode - 消失的数字)

解题思路

暴力思路(不推荐)

最优解:异或(XOR)

异或原理详解

扩展:单身狗问题

[LeetCode - 轮转数组](#LeetCode - 轮转数组)

解题思路

暴力:逐个移位(超时)

最优解:三次反转

关键细节

总结


1,LeetCode - 消失的数字

LeetCode - 消失的数字

数组 nums 包含从 0n 的所有整数,但其中缺了一个,请找出那个缺失的整数。要求 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  ✓
cpp 复制代码
int 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 = a 0 与任何数异或为本身
交换律 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 ]

cpp 复制代码
void 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)

是用简单操作的组合达到线性时间,是位运算和双指针的经典入门题,面试高频出现。

相关推荐
菜菜的顾清寒1 小时前
力扣100(20)旋转图像
算法·leetcode·职场和发展
Navigator_Z1 小时前
LeetCode //C - 1025. Divisor Game
c语言·算法·leetcode
深念Y1 小时前
王者荣耀与英雄联盟数值设计对比:穿透、乘算与加算、增伤乘算更厉害,减伤加算更厉害
数学·算法·游戏·建模·游戏策划·moba·数值
budingxiaomoli2 小时前
优选算法-多源bfs解决拓扑排序问题
算法·宽度优先
隔壁大炮2 小时前
10.PyTorch_元素类型转换
人工智能·pytorch·深度学习·算法
海清河晏1112 小时前
数据结构 | 顺序栈
数据结构
The Chosen One9852 小时前
算法题目分享(二分算法)
算法·职场和发展·蓝桥杯
爱编码的小八嘎2 小时前
C语言完美演绎8-19
c语言
疯狂打码的少年2 小时前
数据结构图的存储方式:从邻接矩阵到十字链表,一文打尽
数据结构·链表