位运算判断重复的核心是用整数每位作布尔开关:小范围(如26字母)用单int位图,通过mask&(1<<i)检测、mask|=(1<<i)置位;大范围需分组(如long数组);唯一重复数可用逐位统计差或异或抵消法。

位运算判断“数值变量是否存在重复的二进制位模式”,关键不在于比较两个数是否相等,而在于识别:**同一组数值中,是否有某个二进制位被多次“点亮”(即多个数在该位上均为 1)**。这常见于两类实战场景:一是检测某类取值范围内是否存在重复元素(如小写字母、0~63 的整数),二是从一组数中反推唯一重复出现的数(如 1~n 中某数多出现一次)。核心思想是用整数的每一位充当一个布尔开关,实现紧凑、高效、无额外容器的标记与校验。
用单个整数做“26字母位图”
适用于字符集固定、范围小(≤32 或 ≤64)的情形,比如判断字符串中哪些小写字母重复出现过,或是否所有字母都不重复。
- 初始化一个 int 变量 mask = 0,它天然表示 32 个“未出现”状态
- 对每个字符 c,计算偏移量 i = c - 'a'(0~25),构造标志位 1 << i
- 检查该位是否已为 1:if ((mask & (1 << i)) != 0) → 已存在,即发现重复
- 否则置位:mask |= (1 << i)
例如字符串 "abca":处理 a→mask=0001,b→mask=0011,c→mask=0111,再遇 a 时 mask & 0001 ≠ 0,立刻返回 true。
分组处理大范围整数(如 0~1000)
当数值可能超过 32 或 64(如含负数、大于 63 的正数),单个整数无法覆盖全部位置,需按位宽分组使用长整型数组。
- 确定每组容量,如用 long(64 位),则每组覆盖 64 个连续整数
- 对每个数 x,计算组号 group = (x - minVal) / 64,组内偏移 bitIdx = (x - minVal) % 64
- 维护 long[] bits = new long[groupCount],检查 (bits[group] & (1L << bitIdx)) != 0
- 若为真则重复;否则执行 bits[group] |= (1L << bitIdx)
例如数组 [-10, 3, 30, 65],minVal = -10 → 映射为 [0,13,40,75],前三个落入 group 0,65 落入 group 1;第二次遇到 3 时,对应位已在 group 0 中置 1,即可捕获重复。
逐位统计法还原唯一重复数(如 1~n 中多一个)
当已知数组长度为 n+1、含 1~n 各一次 + 一个重复数 x,且不能用哈希表或排序,可用“按位计数差”定位 x。
- 遍历 0 到 31 位(覆盖 int 范围),对每位 i 统计两组 1 的个数:
cntArr = nums 中第 i 位为 1 的元素个数
cntNum = [1, n] 中第 i 位为 1 的元素个数 - 若 cntArr > cntNum,说明重复数 x 的第 i 位必为 1
- 将所有满足条件的位组合起来:result |= (1 << i)
原理:除 x 外,其余数在 [1,n] 和 nums 中出现次数相同,其各位贡献抵消;只有 x 多出一次,使对应位总 1 数超标。时间 O(32×n),空间 O(1)。
异或抵消法快速定位偶数次异常
适用于“其他数都出现偶数次,仅一个数出现奇数次(含两次)”的变体,但注意:纯异或无法直接判重,需配合前提约束。
- 若数组为 [1,2,…,n,x],共 n+1 个数,且 x ∈ [1,n],则:
x = (1^2^…^n) ^ (nums[0]^nums[1]^…^nums[n]) - 因为 a^a = 0,a^0 = a,所以所有非重复数成对抵消,只剩 x
- 此法要求其余数恰好出现一次,x 出现两次;若 x 出现三次,则结果为 x 本身(因 x^x^x = x),仍可识别,但不区分“两次”还是“奇数次”

















