剑指 Offer 56 - I. 数组中数字出现的次数 - 力扣(LeetCode)
思路:
由于这个数组中有两个数字只出现了一次,其余数字都出现了两次,而根据按位异或的特性知道,两个相同的数字按位异或的结果是0,相异的数字按位异或是1,任何数与0按位异或都等于这个数本身,正因为其他数字都出现了两次,所以出现了两次的数字全部按位异或得到的结果是0,所以我们把这个数组的所有元素按位异或到一起得到的结果是这两个只出现了一次的数字按位异或的结果ret,这时我们需要把这两个数字分开。由于变量在内存中是以二进制的形式存储的,而二进制只有0和1,所以我们需要找到这个ret任意一个二进制位为1的位置m,由于ret是两个不同的数字按位异或得到的结果,而位置m是1,所以这两个不同的数字的位置m一定不同,而相同的数字位置m的值一定相同,不影响结果,所以遍历这个数组,把数组中m位置二进制位为1的所有元素按位异或到一起的结果就是其中一个只出现了一次的数字,再把数组中m位置二进制位位0的所有元素按位异或到一起的结果就是另一个只出现了一次的数字。
建议参考下面代码梳理上述思路。
int main() { int arr[] = { 1,2,3,4,5,1,2,3 }; int sz = sizeof(arr) / sizeof(arr[0]); int i = 0; int ret = 0; for (i = 0; i < sz; i++) { ret ^= arr[i];//ret得到的结果是两个“单身狗”按位异或的结果 } //分离 int m = 0; for (i = 0; i < 32; i++)//一个整形再二进制里有32个比特位,故需要遍历32个位置,但找到一个1 //之后就无需再找了 { if (((ret >> i) & 1) == 1) { m = i;//m的位置为1,由于ret的m位置为1,而ret是有两个不同的数字按位异或得到的,所以 //这两个数字的m位置的值一定不同 break; } } int x = 0; int y = 0; for (i = 0; i < sz; i++) { if (((arr[i] >> m) & 1) == 1) { x ^= arr[i];//由于x是0,所以不影响结果,得到的结果是其中一个“单身狗” } else { y ^= arr[i];//得到的结果是另一个“单身狗” } } printf("x=%d,y=%d", x, y); return 0; }
如果是再力扣上做题,我们应该改一下形式,如下面的代码。
int* singleNumbers(int* arr, int sz, int* returnSize){ int* p=(int*)malloc(sizeof(int)*2); *returnSize=2; int ret = 0; int i = 0; for (i = 0; i < sz; i++) { ret ^= arr[i]; } //分离 int m = 0; for (i = 0; i < sz; i++) { if (((ret >> i) & 1) == 1) { m = i; break; } } int x = 0; int y = 0; for (i = 0; i < sz; i++) { if (((arr[i] >> m) & 1) == 1) { x ^= arr[i]; } else { y ^= arr[i]; } } p[0]=x; p[1]=y; return p; }