一个数异或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;
}