概要:
1、常见的位运算及距离
2、用异或运算交换两个数
3、某大厂面试题(***)
1、常见的位运算及举例:
2、异或运算交换两个数
3、问题描述:
(1)有101个整数,其中有50个数出现了两次,1个数出现了一次, 找出出现了一次的那个数。
(2)有102个整数,其中有50个数出现了两次,2个数出现了一次, 找出出现了一次的那2个数。
(3)有103个整数,其中有50个数出现了两次,3个数出现了一次, 找出出现了一次的那3个数。
要求:不能开数组,即原地找出结果。
分析:主要用异或运算来解决,首先要知道异或的三个性质:
(1)任何数和0异或结果是本身
(2)任何数和自己异或结果是0
(3)异或运算有交换律
根据这几个特点容易解出第一题:将101个数异或,其结果就是要求的那个数。
int get_xor(int array[], int size) { //int array[SIZE] = { 1, 2, 3, 4, 5, 6,1, 2, 3, 4, 5 }; int xor = 0; for (int idx = 0; idx != size; ++idx) { xor ^= array[idx]; } //printf("唯一的数为: %d\n", xor); return xor; }
接下来看第二小问:由于有两个不同的数,我们想办法把所有数分成两堆,两个不同的数分别在两个堆中,再运用第一问方法求出两个不同的数即可。
分堆方法:因为有两个不同的数,所有数异或结果必有一位是1,我们找最低位1用来分堆,先找到xor的二进制位中最低位为1的位,再根据该位将数组中的数据分成两类,
其中一类该位为1,另一类该位为0
void find2(int array[], int size) { int xor = get_xor(array, SIZE + 1); //int mask = 1; //方法一 O(n) //while (!(xor & mask)) // mask <<= 1; int mask = xor & (~xor +1);//方法二 O(1) int number1 = 0, number2 = 0; for (int idx = 0; idx != SIZE + 1; ++idx) { if (array[idx] & mask) { number1 ^= array[idx]; } else number2 ^= array[idx]; } printf("number1 = %d, number2 = %d\n", number1, number2); }
接下来看第三问,有三个不同的数,如果直接全部异或,必定不行,因为三个不同的数异或,不像两位异或一样必有一位是1,其结果可能有多种。
思想:有三个不同的数,其异或结果可能为0、1,所以先把所有数分成两堆。
目的:将3个不同的数,分到一个奇数堆和一个偶数堆,这样再利用(1)、(2)的结果就可以区分出三个数。
方法:用不断左移的1去和所有数异或。如果是1分到第1堆,如果是0分到第二堆。直到分堆成功(因为有可能所有数的某位都一样)。
注意有可能一个1会把三个数分到同一堆,需要对偶数堆进行异或,若结果为0则说明分堆失败。
#include <stdio.h> #define SIZE 11 int get_xor(int array[], int size) { //int array[SIZE] = { 1, 2, 3, 4, 5, 6,1, 2, 3, 4, 5 }; int xor = 0; for (int idx = 0; idx != size; ++idx) { xor ^= array[idx]; } //printf("唯一的数为: %d\n", xor); return xor; } void find2(int array[], int size) { int xor = get_xor(array, SIZE + 1); //int mask = 1; //while (!(xor & mask)) // mask <<= 1; int mask = xor & (~xor +1);//O(1) int number1 = 0, number2 = 0; for (int idx = 0; idx != SIZE + 1; ++idx) { if (array[idx] & mask) { number1 ^= array[idx]; } else number2 ^= array[idx]; } printf("number1 = %d, number2 = %d\n", number1, number2); } //思想:有三个不同的数,其异或结果可能为0、1,所以先把所有数分成两堆。 //目的:将3个不同的数,分到一个奇数堆和一个偶数堆,这样再利用(1)、(2)的结果就可以区分出三个数。 //方法:用不断左移的1去和所有数异或。如果是1分到第1堆,如果是0分到第二堆。直到分堆成功(因为有可能所有数的某位都一样) //注意:有可能一个1会把三个数分到同一堆,需要对偶数堆进行异或,若结果为0则说明分堆失败。 void find3() { int array[SIZE + 2] = { 1, 2, 3, 4, 5, 6, 1, 2, 3, 4, 5 , 7, 8}; // int heap1[SIZE + 2] = { 0 }; int heap2[SIZE + 2] = { 0 }; int count1 = 0, count2 = 0; int xor1 = 0, xor2 = 0; for (int idx = 0; idx < 32; ++idx) { for(int j = 0; j < SIZE + 2; ++j) { if (array[j] & (1 << idx)) { xor1 ^= array[j]; heap1[count1] = array[j]; ++count1; } else { xor2 ^= array[j]; heap2[count2] = array[j]; ++count2; } } if (xor1 == 0 || xor2 == 0) continue; if (count1 > 0 && count1 % 2 == 0) { //若count1为偶数,则count2为奇数 printf("第一个数是: %d\n", xor2); find2(heap1, count1); break; } else if(count2 > 0 && count2 % 2 == 0){//若count2为偶数,则count1为奇数 printf("第一个数是: %d\n", xor1); find2(heap2, count2); break; } } } int main(void) { //find1(); //find2(); find3(); return 0; }