OJ查找数字

 

问题一

n个数中有一个数x出现了奇数次, 其他所有数均出现了偶数次, 要求通过O(n)时间复杂度的方法, 求x

解析, 通过异或操作, 每两个相同的数均会被成对消除, 剩下的就是出现奇数次的数

例如, $3$ $3$ $3$ $2$ $2$ $4$ $4$, 异或操作后结果为$3$

int a[n] = {.......}

int r = 0;

for(int i = 0 ; i < n; i++) {
    r ^= a[i];
}

问题二

n个数中有两个数x, y出现了奇数次, 其他所有数均出现了偶数次, 找出x, y

解析, 通过异或操作, 得到一个结果值$A$, 对$A$的二进制位进行分解, 从而对原始数据进行分组异或

例如, $7$ $1$ $5$ $5$ $6$ $6$, 异或后相当于$7$ ^ $1 = 6$, 而$(6){10} = (110){2}$

其从右向左而言, 首个为$1$的位数是第二位

则$x、y$从右向左而言的第二位的值一定是一个为$1$且另一个为$0$, 这样它俩异或操作后值才为$1$

现可根据原数组中每个数字的右起第二位值将其分为两组

右起第二位为$0$ 右起第二位为$1$
$1(001) 、5(101) 、5(101)$ $7(111) 、6(110) 、6(110)$

据此, 就转化为了第一问的问题

#include<stdio.h>

const int n = 6;

int main(void){
    int a[n] = {7, 1, 5, 5, 6, 6}

    int r = 0;

    for(int i = 0; i < n; i++) {
        r ^= a[i];
    }

    // 记录结果值右起第一个为1的位置
    int t = 0;

    for(int i = 0; r != 0; i++) {
        if(r & 1){
            t = i;
            break;
        }
        r >>= 1;
    }

    int x = 0;
    int y = 0;

    for(int i = 0; i < n; i++) {
        // 若a[i]的第t位为1
        if(a[i] & (1 << t)) {
            x ^= a[i];
        }else{
            y ^= a[i];
        }
    }

    printf("%d, %d\n", x, y);
}
  • python
n = 6

a = [7, 1, 5, 5, 6, 6]

r = 0

for i in range(n):
    r ^= a[i]

t = 0
while(r):
    if r & 1:
        break
    t += 1
    r >>= 1
    
x, y = 0, 0
for i in range(n):
    if a[i] & (1 << t):
        x ^= a[i]
    else:
        y ^= a[i]

print(x, y)