前言
这里我将分享几道编程题目,主要使用移位和位操作符进行解答。
我们首先要知道两个数字进行异或操作的时候结果是位0的,即 a ^ a = 0,并且我们还要知道 0 和任何数字异或的结果都是这个数字的本身,即 0 ^ a = a;
交换变量(一道变态的面试题)
不能创建临时变量(第三个变量),实现两个数的交换
解析
既然不能创建变量,那么我们可以尝试从位操作符入手,首先 a ^ b 的结果放入 a 中,再异或 b 就会得到原先 a 的值,把异或的结果放入 b 中, a 异或这个 b 的话,就会得到这个 b 的值。
代码实现
#include <stdio.h>int main()
{int a = 5, b = 10;a = a ^ b; // 注意斜杠后面的 a 和 b 是原先的值b = a ^ b; // a ^ b ^ b = a ^ 0 = aa = a ^ b; // a ^ b ^ a = b ^ 0 = bprintf("%d\n%d\n", a, b);return 0;
}
求一个整数储存在内存中二进制中 1 的个数
方法一
我们可以使用一个 1 进行左移操作,遍历三十二位 (y因为整型是四个字节就是32个比特位),用计数器count 来记录有多少个 1 ,这样就可以了
#include <stdio.h>int main()
{int n;scanf("%d", &n);int count = 0;for (int i = 0; i < 32; i++){if (n & (1 << i)){count++;}}printf("%d\n", n);return 0;
}
方法二
我们可以使用一个公式就是 n & (n - 1) , 这个公式能帮我们去掉每一位上的 1 ,这样的话,我们就可以用循环来遍历待测的数字的二进制上的每一位。
#include <stdio.h>int main()
{unsigned int n;scanf("%d", &n);int count = 0;while (n){n = n & (n - 1);count++;}printf("%d\n", n);return 0;
}
你们猜猜看为什么我要使用unsigned ,因为害怕整型溢出,很简单如果 n 的二进制位是32个1,是个负数,你减去一就会整型溢出,所以为了避免这一种情况出现,我们使用unsigned 来接受 n 的数值。
二进制某位上置位 0 或 1
将数字13的二进制序列的第五位设置为1,之后再改为0.
解析
这道题很简单,对 1 进行移位操作,将结果与13 进行a按位或( | )操作即可。再改为 0 也是一样的,就是将 1 移位操作再按位取反加 ( & )操作即可。
代码实现
#include <stdio.h>int main()
{int n = 13;n = n | (1 << 4);printf("%d\n", n);n = n & (~(1 << 4));printf("%d\n", n);return 0;
}
单身狗1
在一个整型数组中,只有一个数字出现一次,其他数组都是成对出现的,请找出那个只出现一次的数字。
例如:
数组中有:1 2 3 4 5 1 2 3 4,只有5出现一次,其他数字都出现2次,找出5
解析
我们设置一个 tmp 为0,然后用循环来遍历每一个数字与tmp进行异或,最后打印tmp的值就是那个“单身狗”!
代码实现
#include <stdio.h>int main()
{int arr[9] = { 1,2,3,4,5,1,2,3,4 };int sz = sizeof(arr) / sizeof(arr[0]);int i = 0, tmp = 0;for (i = 0; i < sz; i++){tmp ^= arr[i];}printf("%d\n", tmp);return 0;
}
单身狗2
一个数组中只有两个数字是出现一次,其他所有数字都出现了两次。
编写一个函数找出这两个只出现一次的数字。
例如:
有数组的元素是:1,2,3,4,5,1,2,3,4,6
只有5和6只出现1次,要找出5和6.
解析
在有了单身狗1 的基础上,我们知道所有数字异或之后就会找到那个落单的数字,但是这道题是有两个落单的数字,那么我们将所有数字进行异或后,得到的结果就是这两个落单的数字异或的结果。既然如此,这个结果能不能进行二次加工来分离这两个数字呢?在二进制的基础上,异或是相同的为0,不同的为1,两个不同的数字必然在某些二进制位是不同的,那么我们就可以将这个结果第几位为 1 作为分界线,来区分这两个数字,我们可以将它们分开来,然后对这两组进行分别异或,就会得到这两个数字。
代码实现
#include <stdio.h>void Find_Single_Dog(int* p, int n, int *num1, int *num2)
{int tmp = 0;int i = 0;for (i = 0; i < n; i++){tmp ^= p[i];}int k = 0;for (i = 0; i < 32; i++) //找到第几位为1{if (tmp & (1 << i)){break;}}k = i;for (i = 0; i < n; i++){if (p[i] & (i << k)){*num1 ^= p[i];}else{*num2 ^= p[i];}}
}int main()
{int arr[] = { 1,2,3,4,5,1,2,3,4,6 };int sz = sizeof(arr) / sizeof(arr[0]);int num1, num2;num1 = num2 = 0;Find_Single_Dog(arr, sz, &num1, &num2);printf("%d\n%d\n", num1, num2);return 0;
}