问题1
已知在一个数组中,只有一种数出现了奇数次,其它所有数都出现偶数次,怎么找到出现奇数次的数?
思路
利用异或的性质,参考这篇文章中对异或性质的解释,可以知道,只需要将这个数组的所有数挨个异或一遍,出现偶数次的数异或之后为0,最后仅剩下出现奇数次的那个数字本身。
代码
void printOddTimesNum1(vector<int>& arr)
{
int eor = 0;
for (auto &curNum : arr)
{
// 挨个进行异或操作
eor ^= curNum;
}
cout << "The only odd is: " << eor << endl;
}
void main()
{
// 仅5出现3次,其它数均出现2次
vector<int>arr1{ 1,1,3,4,3,5,5,7,5,4,7 };
printOddTimesNum1(arr1);
system("pause");
}
问题2
已知在一个数组中,只有两种数出现了奇数次(两种数不相等),其它所有数都出现偶数次,怎么找到出现奇数次的数?
思路
同样利用异或的性质,各个代码的含义和思路都写在代码注释中了,可以对应着来看。
代码
void printOddTimesNum2(vector<int>& arr)
{
// 思路:
// 不妨设出现奇次数的两种数为a和b,那么此时得到的结果eor为a^b,eor中至少有1位,不妨设h位不为0
// 那么两个出现奇次数的数在h位上的必然不同(必然一个是0一个是1)
// 然后,将h位上为0或为1的所有的数异或一遍,此时必然只能得到a或者b中的一个
// 然后通过a^b^a或者a^b^b就能得到另一个数
int _eor = 0;
for (auto& curNum : arr)
{
_eor ^= curNum;
}
// 此时得到的_eor是a^b
int rightOne = _eor & (~_eor + 1);
// 解释:
// ~eor按位取反之后,从右数第一个1之后全部都是0,所以~eor表示的是....01111....1
// ~eor+1得到....10....0,也就是说,此时得到的这个数,在原数字从右数第一个1位置上也为1
// 而往左看,每一位都与原数字不同,往右看,每一位都为0
// 两者做按为交操作,则得到的结果只有原数从右数第一个1位置处为1,其余位置均为0
int eor = 0;
for (auto& curNum : arr)
if ((rightOne & curNum) == 1)
// 由于rightOne只有一个位置为1,其它位置都为0,所以与rightOne按位交,结果是0还是1取决于curNum对应位是0还是1
eor ^= curNum;
// 此时得到的eor是a或者b
cout << "The first odd is: " << eor << ", and the second odd is: " << (_eor ^ eor) << endl;
}
void main()
{
// 仅5和6出现3次,其它数均出现2次
vector<int>arr2{ 1,1,3,4,3,5,5,7,5,4,6,6,7,6 };
printOddTimesNum2(arr2);
system("pause");
}