位运算:找出来数组中落单的两个数

题目大意

每次给你 2 * n + 2 个的数字,除其中两个数字之外其他每个数字均出现两次,找到这两个数字并升序输出。

思路
最简答的想法,排序,然后找出只出现一次的数,不再叙述。
来说一说用位运算怎么解决:

假设这两个不同的数 x2 = 1100101 和 y2 = 1010001。
观察标记出来的位置。易知,对于两个不同的数,一定有一个位置 pos 对应这两个数在二进制下是不同的。 上边的例子中,pos = 3

我们这里先讲两个前导知识:
0 ^ x = x,x 异或 0 的结果还是 x
x ^ x = 0,两个相同的数异或结果为 0
根据这两个性质,我们可以在 2 * n + 1 个数中,轻易的找出落单的一个数。eg: 1 ^ 1 ^ 2 ^ 2 ^ 3 = 3

我们结合刚刚的结论,如何用前导知识来找出落单的两个数。
我们就刚刚的 x 和 y 来讲,x 对应的二进制数下第 3 位是 1,而 y 对应的二进制数下第 3 位是 0。
要是我们把这 2 * n + 2 中,二进制数下第 3 位是 1 的所有数都拿过来,就会变成这样:
x   = 110 0101
a1 = 110 1101
a2 = 110 1101
a3 = 101 1101
a4 = 101 1101
很明显,x ^ a1 ^ a2 ^ a3 ^ a4 = x。

同样的对于二进制数下第 3 位是 0 的所有数:
y   = 101 0001
a5 = 110 1001
a6 = 110 1001
a7 = 101 1001
a8 = 101 1001
y ^ a5 ^ a6 ^ a7 ^ a8 = y。

也就是,我们对 2 * n + 2 个数,依据在 pos 下的数值分组即可。
现在的问题就是,如何找到 x 和 y 对应的 pos。

这里再来一个前导知识,x & (x-1) 的结果为:x 减去其二进制下从右往左数的第一个 1.
eg: x = 11010,那么 x & (x-1) = 11000
如果我们让 x 减去 x &(x-1), 这时的结果为:x 对应二进制下第一个 1 表示的数
eg: x = 11010,x - (x & (x-1)) = 00010
ps:位运算的优先级是最低的,记得加括号。x - (x & (x-1)) = x & (x^(x-1)),有兴趣自己证明吧

异或运算是相同位为 0, 不同位为 1。
令 z = x ^ y = 1100101 ^ 1010001 = 110100。
观察 z 我们可以发现,z 在二进制下从右往左的第一个 1 就是 x 和 y 对应的 pos。

到这里这个问题就结果了,具体看代码吧:

代码

#include<stdio.h>

int a[10005]; 

int main(){
    int n, i;
    scanf("%d", &n);
    n = n * 2 + 2;
    for(i = 0; i < n; i++) scanf("%d", &a[i]);
   	int z = 0;
	for(i = 0; i < n; i++) z ^= a[i];	 
    // 对所有数异或,相同的数异或结果位 0,最终的z = x ^ y
	
	int pos = z - (z & (z-1));
	// 找到 z 在二进制下第一个 1,也就是 x 和 y 在二进制下第一个不同的位置 
	
	int x = 0;
	for(i = 0; i < n; i++){
		if((pos & a[i]) != 0){ // 找到所有的在 pos 这个位置为 1 的数 
			x ^= a[i];
		}
	} 
	// 解释一下 if 判断, 100 & 101 = 100, 100 & 001 = 0, 达到一个分组的目的
	
	int y = z ^ x;	// z = y ^ x, z ^ x = y ^ x ^ x = y
	
	if(x > y) printf("%d %d\n", y, x);
	else printf("%d %d\n", x, y);
    
    return 0;
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值