只出现一次的数字(位运算)

一个数异或0结果为它本身,一个数异或它本身结果为0,而且异或运算满足a^b^c=a^(b^c);

 

题目一:给定一个非空整数数组,除了一个元素只出现一次以外,其余每个元素均出现两次。找出那个只出现了一次的元素。

思路:设置一个变量ret异或每个数组元素,最后相同的都抵消为0,那个唯一的数字异或0为它自己即为答案。

代码如下:

#include<stdio.h>
int main()
{
    int nums[5]={4,1,2,1,2};
    int ret=0,i=0;
    for(i=0;i<5;i++)
        ret^=nums[i];
    printf("%d",ret);
	return 0;
}

题目二:给定一个非空整数数组,有两个元素只出现一次以外,其余每个元素均出现两次。找出只出现了一次的元素。

思路:这道题和上一题不一样的是有两个元素只出现了一次,有一个元素出现一次的问题我们可以解决,那么我们就要想办法把这道题转化成第一题的样子,我们可以把该数组转化成两个子数组,每个数组里有一个出现一次的元素,接下来我们就会了;难点就在于如何转化两个子数组。

先举出一个数组:{2,3,4,1,2,4};

先把二进制列出来:

2:0010
3:0011   
4:0100  
1:0001  
2:0010  
4:0100 

整个数组的异或结果:0010;不难发现将整个数组异或的话,最后的结果是单独出项的那两个数异或的最终结果!

最终异或结果(过程就不列了):0010    那么第二位是1,则说明两个单独出现的数的第二位不同;
那么我们可以根据这个条件对数组进行分组;第二位为1的为一组,第二位为0的为一组;然后每一组进行异或,
得到的两个结果就是单独出现的俩个数!(注意:我举得例子比较简单,如果最终异或结果有多个1,我们只根据
第一个出现的1的位进行分组);

 

#include<stdio.h>
#include<stdlib.h>
 
void FindOnce_Number(int arr[], int sz)
{
	int i = 0;
	int j = 0;
	int num1 = 0;
	int num2 = 0;
 
	int tmp = 0;
 
	for(i = 0; i < sz; i++)
		tmp ^= arr[i];//整个数组的异或结果既是两个单独数异或结果;
	for(i = 0; i < 32; i++)
	{
		if(((tmp>>i)&1) == 1)
			break;//找到第一个为1的位,并记住;
	}
	for(j = 0; j < sz; j++)
	{
		if(((arr[j]>>i)&1) == 0)//第i位为0的为一组全部异或;
			num1 ^= arr[j];
		else
			num2 ^= arr[j];//剩下的则是第i位为1的,为一组全部异或;
	}
	//最后的结果就是num1 和 num2;
	printf("单独出现的两个数分别是:%d 和 %d\n",num1,num2);
}
 
int main()
{
	int arr[] = {2,3,4,1,2,4};
	int sz = sizeof(arr)/sizeof(arr[0]);//测数组长度,不可以用strlen函数
 
	FindOnce_Number(arr,sz);
 
	system("pause");
	return 0;
}

题目三:给定一个非空整数数组,有元素只出现一次以外,其余每个元素均出现三次。找出只出现了一次的元素。

思路:这是前一道题的一个变种,用第一道题的方法行不通,仔细分析;

@以{2,3,2,4,2,4,4}为例

2:0010
3:0011
2:0010
4:0100
2:0010
4:0100
4:0100

这样列出来的话,可能半天看不出来有什么特点,那么这样列出来呢:

2:0010
2:0010
2:0010
4:0100
4:0100
4:0100
3:0011

这样列出来的话可以发现,如果没有3的话,出现三次的数的二进制上的1就能被3整除,
那么,加入了3之后是不是破坏了原有的平衡,只要找出每一位1的出现不能被3整出的位,那单独
出现一次的数的这一位肯定是1,其他位如果1只出现了一次,那肯定是单独数上的1;那么,最后就可以得出结果了。

语言叙述不太明白,具体看代码:

#include<stdio.h>
#include<stdlib.h>
#include<string.h>

//难点在于找出所有数的所有位的1的个数,用较为简单的方法,以下方法为我学习大神的方法!特别棒!
int FindOnce_Number(int arr[], int sz)
{
    int bit[32];
    int i = 0;
    int j = 0;
    int num = 0;
    memset(bit,0,32*sizeof(int));//将数组清零,内存操作函数,头文件<string.h>,作用是将数组bit全部置0;
    for(i =  0; i < sz; i++)
    {
        for(j = 0; j < 32; j++)
        {
            bit[j] += ((arr[i]>>j)&1);
        }
    }
    
    for(j = 0; j < 32; j++)//32的话表示最多二进制有32位就够了
    {
        if(bit[j]%3 != 0)
        {
            num |= (1<<j);
        }
    }
    //bit[0]表示数组中所有数转换成二进制后,从右往左第0列中所有1的个数,往后以此类推
    return num;
}

int main()
{
    int arr[] = {2,3,2,4,2,4,4};
    int sz = sizeof(arr)/sizeof(arr[0]);//测数组的长度,不能用strlen
    int num = FindOnce_Number(arr,sz);
    printf("出现一次的数是:> %d\n",num);
    system("pause");
    return 0;
}

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值