位操作符是针对二进制位进行计算的,他们的操作数必须是整数
目录
例1:交换两个变量(不创建临时变量)
我们首先要了解异或 ^ 的3个特点:1. a ^ a = 0 2. 0 ^ a = a 3. 异或支持交换律
代码这样写:
#include <stdio.h>
int main()
{
int a = 10;
int b = 20;
printf("交换前:a = %d b = %d\n", a,b);
a = a^b;//(1)
b = a^b;//(2)
a = a^b;//(3)
printf("交换后:a = %d b = %d\n", a,b);
return 0;
}
(2):b = (a^b)^b = a^(b^b) = a^0 = a (3):a = (a^b)^b = (a^b)^a = 0^b = b 就完成了交换
例2:统计二进制中1的个数
主函数如下,各个方法只展示调用函数部分
int main()
{
int n = 0;
scanf("%d", &n);
int ret = NumberOf1(n);
printf("%d\n", ret);
return 0;
}
法1:得到二进制的每一位
我们将它与如何得到十进制作对照:
int NumberOf1(unsigned int n)
{
int count = 0;
while (n)
{
if (n % 2 == 1)
count++;
n = n / 2;
}
return count;
}
法2:右移>> 与&1
注意到 与&:对应二进制位有0,则为0;同时为1才是1
我们把这个数右移>> 32次,每次&1的结果进行判断,结果为1,计数
int NumberOf1(int n)
{
int i = 0;
int count = 0;
for (i = 0; i < 32; i++)
{
if (((n >> i) & 1) == 1)
{
count++;
}
}
return count;
}
法3:n & ( n - 1 )
与1次,计数,直至为0
int NumberOf1(int n)
{
int count = 0;
while (n)
{
n = n & (n - 1);
count++;
}
return count;
}
你可能会有疑问:要是n为负数,循环怎么停止?
解答:假设一个负数
n 1100 0000 0000 0000 0000 0000 0000 0000
n-1 1011 1111 1111 1111 1111 1111 1111 1111
n & n - 1 1000 0000 0000 0000 0000 0000 0000 0000 (此时为负数最小值)
继续进行
n 1000 0000 0000 0000 0000 0000 0000 0000
n-1 0111 1111 1111 1111 1111 1111 1111 1111
n & n - 1 0000 0000 0000 0000 0000 0000 0000 0000
这个时候就是0了
疑问:那倒数第三行的n-1,符号位为0,不就是正数了吗
解答:对呀,因为此时n已经是当前32位能表示的最小值了,-1一定是超过了这个范围的,就会循环,成为正数最大值,然后&的时候就没了
例3:求两个数二进制中不同位的个数(32)
主函数部分:
int main()
{
int m = 0;
int n = 0;
scanf("%d %d", &m, &n);
int ret = count_diff_one(m ,n);
printf("%d\n", ret);
return 0;
}
法1:右移>> 与&1
这个和例2,法2相似
int count_diff_one(int m, int n)
{
int i = 0;
int count = 0;
for (i = 0; i < 32; i++)
{
if (((m >> i) & 1) != ((n >> i) & 1))
{
count++;
}
}
return count;
}
法2:异或 ^
思路:
- 先将m和n进行按位异或,此时m和n相同的二进制比特位为0,不同的为1
- 统计异或后的二进制中,有多少个1(回到了例2)
int count_diff_one(int m, int n)
{
int count = 0;
int tmp = m ^ n;
//统计tmp的二进制中有几个1
while (tmp)
{
tmp = tmp & (tmp - 1);
count++;
}
return count;
}
例4.交换奇偶位(用宏)
写一个宏,可以将一个整数的二进制位的奇数位和偶数位交换。交换第1 2,3 4,5 6......31 32位
思路:拿出所有奇数位,左移1;拿出所有偶数位,右移1。结果再相加
如何拿出所有奇/偶位?
eg:1 1 1 1 1 1 1 1 奇数位、偶数位
拿出奇数位:
1 1 1 1 1 1 1 1
0 1 0 1 0 1 0 1 (16进制:0x55)与 & :0 1 0 1 0 1 0 1 —— (a&0x55555555)<<1
拿出偶数位:
1 1 1 1 1 1 1 1
1 0 1 0 1 0 1 0 (16进制:0xaa)与 & :1 0 1 0 1 0 1 0 —— (a&0xaaaaaaaa)>>1
#define SWAP(x) (x=(((x&0x55555555)<<1)+((x&0xaaaaaaaa)>>1)))
int main()
{
int a = 10;
SWAP(a);
printf("%d\n", a);
return 0;
}
例5.找单身狗
版本1:
有一个数组,其中只有一个数字出现1次,其余数字出现2次。编写一个函数找出这两个只出现一次的数字。
eg:1 2 3 4 5 1 2 3 4
思路:把所有数字异或^在一起 a ^ a = 0 , 0 ^ a = a
版本2:升级
一个数组中只有两个数字是出现一次,其他所有数字都出现了两次。编写一个函数找出这两个只出现一次的数字。
eg:1 2 3 4 5 1 2 3 4 6
法1:遍例
缺点:效率低,100个数字要遍例10000次。
int main()
{
int arr[] = { 1,2,3,4,5,1,2,3,4,6 };
int len = sizeof(arr) / sizeof(arr[0]);
int i = 0;
int count = 1;
for (i = 0; i < len ; i++)
{
count = 1;
int j = 0;
for (j = 0; j < len ; j++)
{
if (arr[i] == arr[j])
{
count++;
}
}
if (count == 2)
{
printf("%d ", arr[i]);
}
}
法2:分组
我们想:能不能分组,各组都满足版本1。即:每组只有1个单身狗,剩下的都是成对出现的数字。
分组方法不同,分出每组的数据不同。
怎么分?以eg为例,找 5,6 的差异在哪
^ :二进制位,相同为0,相异为1
由于 5 != 6 ==> 5 ^ 6 != 0 ==> ^ 的结果里一定有1
5:1 0 1
6:1 1 0
^ :0 1 1
总结:两个二进制数,^ 结果为1 的二进制位,记为 pos 。两个二进制数的 pos 位一定不相同
以 ^ 结果为1的二进制位数进行分组 ^ 结果为0的不管
eg中,以最低位为1的放到一组:5 1 1 3 3
0 :6 2 2 4 4
eg中,以第二位为1的放到一组:6 2 2 3 3
0 :5 1 1 4 4 (5,6 必然在2个组里)
以上2种分组,各组都满足版本1的形式。再将2组中,每组 ^ ,便可得到2个单身狗
//函数不能同时返回2个值,所以返回类型先写为void
void find_single_dog(int arr[], int sz, int single_dog[])
{
}
int main()
{
int arr[] = { 1,2,3,4,5,1,2,3,4,6 };
int sz = sizeof(arr) / sizeof(arr[0]);
int single_dog[2] = { 0 };
find_single_dog(arr, sz, single_dog);
printf("%d %d\n", single_dog[0], single_dog[1]);
return 0;
}
我们跳出以上分析,从头开始。
现在要把拿到数组里的2个单身狗 ^ 在一起,但我不知道谁是单身狗呀
所有数据都 ^ 在一起 <==> 2个单身狗 ^ 在一起
看 ^ 结果,计算 ret 的二进制位里,哪一位第一次是1,记为 pos。2个单身狗的 pos 位一定不相等,再对每组 ^ 即可得到2个单身狗
怎么算?
例2 法2:右移>> 与&1 总共才32个二进制位,循环走起
void find_single_dog(int arr[], int sz, int single_dog[])
{
int ret = 0;
int i = 0;
for (i = 0; i < sz; i++)
{
ret ^= arr[i];
}
//计算ret的二进制位里,哪一位是1
int pos = 0;
for (i = 0; i < 32; i++)
{
if (((ret >> 1) & 1) == 1)
{
pos = i;
break; //找到第一次出现1的二进制位了,不用再循环了
}
}
//分组
for (i = 0; i < 32; i++)
{
if ((arr[i] >> pos) & 1 == 1)
{
single_dog[0] ^= arr[i];
}
else
{
single_dog[1] ^= arr[i];
}
}
}
int main()
{
int arr[] = { 1,2,3,4,5,1,2,3,4,6 };
int sz = sizeof(arr) / sizeof(arr[0]);
int single_dog[2] = { 0 };
find_single_dog(arr, sz, single_dog);
printf("%d %d\n", single_dog[0], single_dog[1]);
return 0;
}
本篇的分享就到这里了,感谢观看,如果对你有帮助,别忘了点赞+收藏+关注。
小编会以自己学习过程中遇到的问题为素材,持续为您推送文章。