北邮oj 84 single number


时间限制1000 ms 内存限制 65536 KB

题目描述

Given an array with N integers where all elements appear three times except for one. Find out the one which appears only once.

输入格式

Several test cases are given, terminated by EOF.

Each test case consists of two lines. The first line gives the length of array N(1N105) , and the other line describes the N elements. All elements are ranged in [0,2631] .


这道题和leetcode 上的single number 2 算法核心相同,只不过是数组元素的范围更大了。

本来这道题目没有什么特别难的地方,就是想到利用位运算即可。而且北邮oj上的限制不多,利用暴力穷举方法也可以解决。但是由于我过于蠢,前几次的算法都是利用java写的,导致时间总是超时。结果一道可以利用暴力方法写出来的题目,硬生生让我写了两天。同时利用位运算的时候,我用c++的cin和cout,结果时间还是超时了。最后换成了scanf和printf才终于AC了再见。真是好久不写了,第一次写就给我这么深刻的教训。


所谓位运算解法就是把每个十进制的数字看成二进制的,如果是出现两次的只要全部数字异或之后,最后结果就是单独出现的数字。而出现三次也是这个思路,利用一个数组例如num[64]记录各个位上的1出现的个数,然后num[i]=num[i]%(3)。最后将num这个数组转换成十进制数字即可。

而网上有一个位运算简化的代码,但是我在看的时候大部分博主的解释很少或者没有,这下面就是我仿照网上的代码写的。同时附上自己的理解。

#include<stdio.h>
int main() {
	int n = 0, i = 0;
	unsigned long long one, two, three, num;
	while (scanf("%d",&n)!=EOF) {
		one = two = three = 0;
		for (i = 0; i < n; i++) {
			scanf("%lld",&num);
			three = two&num;
			two = two | one&num;
			one = one | num;
			one = one&(~three);
			two = two&(~three);
		}
		printf("%lld\n", one);
	}
	return 0;
}


这个算法实质上还是位运算的方法。这里它有三个记录的标志。one two three,其中one记录数组元素出现一次的位,two记录出现两次的,three记录出现三次的位。

举例来说,假如有4个数字,分别是11,1,1,1

二进制是:1011,0001,0001,0001

当我们一开始读入1011B时,three和two都是0000B,而one这个时候就会变成1011B

第二个数字0001B读入时,three依然是0000B,而two则会变成0001B,这个时候你比较第一次读入的1011B,发现只有最低位出现两次,而two其实就是记录数字各位出现两次的位。其他的也同理。

			three = two&num;
			two = two | one&num;
			one = one | num;

这段代码就是计算one,two,three的代码,可以自己举例好好理解一下。

这就要求我们的计算three,two,one的顺序不能变(存疑,有些代码three在two和one后计算)。而最后的

			one = one&(~three);
			two = two&(~three);

则是在消除one和two中出现三次的位。这样one和two中的就是真正出现一次和两次的位。

而如果单独出现的数字有几位和三次出现的数字重复怎么办?就像我之前举得例子:1011B,0001B,0001B,0001B。这四个数字最低位都是1,这个其实没有关系。因为除了这个单独出现的数字,其他数字都是出现3次,其他数字的总出现的位数一定是3的倍数,而单独出现的数字的位数则一定不是3的倍数。那么最后one一定就是单独出现的数字的位数。


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值