1:数组中只有一个数字出现了奇数次,其余数字出现偶数次,找出这个出现奇数次的数字。
2:数组中有两个数字出现了奇数次,其余数字出现偶数次,找出这两个出现奇数次的数字。
出现如上两种问题或类似问题可以使用 异或的思想求解 ,在使用之前,先了解异或的几种基本性质
1)a ^ b ^ c=a^c ^b 即异或满足交换律,
2)a^ a=0 一个数异或它本身得到的结果为0
3)a^ 0=a 一个数异或0还是等于它本身
很好用来理解异或的例子是 你可以将异或理解为无进位相加:
如:10111^10011=???
10111
^
10011
=00100
也就是如果能够进位,直接将那位写成0就行了,进到前面的1不用管。
ok,有了上述理论基础可以开始解决问题了:
1 在这里插入代码片,
int a[5] = { 0,0,1,1,3 };
int arb = 0;
for (int i=0; i<5; i++)
{
arb = arb ^ a[i];
}
cout << arb;
解析:因为一个数异或它本身是为0的,所以出现偶数次的数都可以消掉,最后只剩下一个出现奇数次的数字,即为所求值。
2在这里插入代码片,
int arr[8] = { 0,0,1,1,2,3,4,4 };
int arb = 0,other=0;
for (int i=0; i<8; i++)
{
arb ^= arr[i];//最后求出的值为2异或上3
}
int rightone = (~arb+1)& arb;
for (int i : arr)
{
if ((rightone & i) == 0) //等于0或者rightone都可以,分出了两种数,一种对应位为1,一种对应位为0
other ^= i;
}
int another = other ^ arb;
cout << other <<" "<< another;
解析:和第一题一样,这道题是求两个奇数次数字,将整个数组的数进行异或,最后得到的值arb为两个奇数次数字异或起来的值,也就是arb=奇数次1数字 ^ 奇数次2数字,接下来需要把这两个数字分开,
只需要再求出其中一个,就能得到另一个,此时引入了&号和~号。
~:取反 如10110 取反为 01001
&:同为1为1,否则为0,10100&11001==10000 101&101=101
因为arb不为0(a不等于b),所以二进制数字中总有一个位置上为1 ,所以a或者b在该对应位置上不相同,一个为1,一个为0,rightone的作用是取出arb二进制数中的最后出现的一个1
假如arb=10110,~arb=01001 (~arb+1)=01010 两者相&得到00010,取出了最后位数的1,若是以后遇到需要取出右边的1时,请直接 取反加1 再 相与&
取出最后的1的作用:利用这个1来分出那个对应位置上为1的数字,这些数字中其中必包含一个我们需要求的奇数次数字a或者b,另一批对应位置为0的数字必包含另一个我们需要的奇数次数字。
所以other最后就是a或者b中的其中一个,再用other ^ arb即可求出另外一个即可
(暂完结)
可以思考下出现三个奇数时有什么方法解,后续会将文章配上图更好的阅读,嘿嘿,重走一回编程路,
`



