找出数组中只出现1次的数(各种变形)

位运算的应用。

一、数组中只有一个数出现1次,其他数字均出现两次。找出只出现1次的那一个数字。

任何数字和本身异或的结果为0。0和某数字的异或结果为其本身。所以数组中所有数字异或结果就是所求结果。代码如下。

int singleNumber1(vector<int>& nums) {
	int ans=nums[0];
	for(int i=1;i<nums.size();i++){
		ans^=nums[i];
	}
	return ans;
}

二、数组中有两个数字出现1次,其他数字均出现两次。找出只出现1次的那两个数字。

这里还是要借鉴第一节中的思路。如果把一个数组分成两个数组,每个数组中分别包含一个只出现1次的数字即可使用第一节中的思路。那么如何把数组分成两个子数组呢?如何两个只出现1次的数组为a和b,那么整个数组的异或结果为xor_res,那么xor_res=a^b。xor_res不等于0。取xor_res二进制表示中最后一位为1的位置(假设第k位,结果九为1<<k),说明a和b在第k位不相同。我们根据第k位是否为1将数组分成两个部分,对每部分分别求只出现1次的数字即可。

int lastBitOf1(int num){
	return num&(~(num-1));
}
vector<int> singleNumber2(vector<int>& nums) {
	int xor_res=0;
	for(int i=0;i<nums.size();i++){
		xor_res^=nums[i];
	}
	int pos=lastBitOf1(xor_res),one=0,two=0;
	for(int i=0;i<nums.size();i++){
		if(nums[i]&pos){
			one^=nums[i];
		}else{
			two^=nums[i];
		}
	}
	vector<int>result_vec;
	result_vec.push_back(one);result_vec.push_back(two);
	return result_vec;
}


三、数组中有三个数字出现1次,其他数字均出现两次。找出只出现1次的那三个数字。

这里的方法很难想出来。http://zhedahht.blog.163.com/blog/static/25411174201283084246412/里面讲的挺好。这里我的实现与他的实现有些不同。他本身的证明后面也有点麻烦。

大致思路为:假设要求数字为a、b和c,整个数组异或结果为xor_res,xor_res=a^b^c。可证xor_res和a、b、c均不相等。(反证法,如果xor_res=a,则a^a=a^b^c^a=0,则b^c=0,说明b和c相等,矛盾)。所以xor_res^a,xor_res^b,xor_res^c均不为0。

f(x)取x最后1位为1的位置。f(xor_res^a)^f(xor_res^b)^f(xor_res^c)不为0。(因为前两部分异或结果的二进制表示只可能有0个1或者2个1,而第三部分二进制表示只有1个1)

f(xor_res^a)^f(xor_res^b)^f(xor_res^c)既然不为0,假设最后第k位为1。f(xor_res^a)、f(xor_res^b)、f(xor_res^c)三部分中只能有一个第k位为1。(三部分也可以写成f(b^c)、f(a^c)和f(a^b),如果每部分第k位都为1,很容易推出矛盾)。

那么三部分中只能有一个第k位为1。铺垫了这么久,也就是为了根据这个先找出一个。然后使用第二节中的方法找出其他两个。

void singleNumber3(vector<int>&nums){
	int xor_res=0;
	for(int i=0;i<nums.size();i++){
		xor_res^=nums[i];
	}
	int temp=0;
	for(int i=0;i<nums.size();i++){
		temp^=lastBitOf1(xor_res^nums[i]);
	}
	int pos=lastBitOf1(temp),first=0;
	vector<int>temp_vec;
	for(int i=0;i<nums.size();i++){
		if(lastBitOf1(xor_res^nums[i])==pos){
			first^=nums[i];
		}else{
			temp_vec.push_back(nums[i]);
		}
	}
	vector<int>result_vec=singleNumber(temp_vec);
	cout<<first<<endl;
	cout<<result_vec[0]<<endl;
	cout<<result_vec[1]<<endl;
}

四、数组中有一个数字出现1次,其他数字均出现三次。找出只出现1次的那一个数字。

这里就无法再使用前面三节中异或运行了。我们换种思路来想,把每个数字用做二进制表示。int的话只需要32位即可,所以我们新建大小为32的数组,把每个数字的对应位加到数组的相应位置上。如果我们把只出现1次的数字(假设为a)去除,那么最后数组里面的每一位都应该是3的倍数。所以最后数组里任一个位模3之后就是a二进制表示中相应位的值。

int singleNumber(vector<int>& nums) {
	vector<int>vec(32,0);
	for(int i=0;i<nums.size();i++){
		for(int j=0;j<32;j++){
			vec[j]+=(nums[i]>>j)&1;
		}
	}
	int ans=0;
	for(int i=0;i<32;i++){
		ans+=((vec[i]%3)<<i);
	}
	return ans;
}

  • 4
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值