01-求一个整数存储在内存中的二进制中1的个数
法一:循环遍历
- 问题引入
以在十进制中,求某一个数字n中的某个数字m的数量,来引入二进制中求1的个数。
要求一个十进制数n中某一位数字m的个数,只需要让n对10取模与m比较、再让n = n / 10,反复循环,直到n等于0即可。 由此推断出求一个整数存储在内存中的二进制中1的个数的求法:
代码实现:
int main () {
int i;
int cut = 0;
scanf("%d", &i);
while (i) {
if (i % 2 == 1) {
cut++;
}
i = i / 2;
}
printf("%d\n", cut);
return 0;
}
// 15 -> 4
上面这种方法看似可以,但其实是有问题的,它不能求负数二进制中1的个数;
以-1举例:我们知道-1的二进制是全1,所以计算结果应该是32,但是如果我们用上面的方法求出来的结果会是0,因为在第一次循环中-1/2等于0会导致直接退出循环;
优化如下:
可以将i赋值给一个无符号整数u_i,如果输入的是一个负数的话,它在赋值的时候会发生隐式类型转换,使得u_i变成一个非常大的正数,然后我们直接对u_i求二进制1的个数即可:
int main () {
int i;
size_t u_i;
int cut = 0;
scanf("%d", &i);
u_i = i;
printf("sizeof(u_i)=%d\n", sizeof (u_i)); // 8
while (u_i) {
if (u_i % 2 == 1) {
cut++;
}
u_i = u_i / 2;
}
printf("%d\n", cut);
return 0;
}
// -1 -> 64
法二:移位相与
思路分析:一个数按位&(与上)一个1得到的结果就是该数最低二进制位所代表的数,同时又知道右移操作符可以让一个数的任意二进制位来到最后一位;所以就可以利用右移+按位与,再配合循环来实现要求:
int main() {
int i;
scanf("%d", &i);
int cut=0;
int sum=0;
for (int j = 0; j < 32; ++j) { // 32位机器为例
sum++;
if(((i >> j) & 1) == 1) {
cut++;
}
}
printf("cut=%d,sum=%d\n", cut,sum);
return 0;
}
上面for循环的作用是让i的每一位二进制位都与1按位求与,如果求得的结果为1(即对应二进制位为1)就让cut++;由于这是直接对内存中的二进制位进行操作,所以不用担心负数会不一样,但是也可以看到,这种方法必须循环32次。
优化1:
让u_i的每一位都与1进行按位与,结果为1的话就让cut++,然后将u_i右移一位之后循环,结束条件是u_i的二进制位都为0即u_i为0或者右移32次之后结束循环。
#include <stdio.h>
int main () {
int i;
size_t u_i;
int cut = 0;
scanf("%d", &i);
u_i = (size_t)i; // 将负数强制转换为无符号整数
while (u_i && cut < 32) { // 判断是否溢出(32机器为例)
if (u_i & 1) {
cut++;
}
u_i >>= 1;
}
printf("%d\n", cut);
return 0;
}
优化2: 与n-1按位求与
循环的让num = num & (num-1),直到num为0时循环结束
这里每一次循环的效果是去掉最低位的一个1,所以这里的循环次数与二进制中1的个数相同。
int main() {
int i;
int cut = 0;
scanf("%d", &i);
int sum=0;
while (i) {
sum++;
cut++;
i = i & (i-1);
}
printf("cut=%d, sum=%d\n", cut,sum);
return 0;
}
02-求二进制中不同位的个数
求两个数二进制中不同位的个数:牛客网链接
只需要让这两个数按位异或,那么异或得到的这个数的二进制中1的个数就是两个数二进制中不同位的个数;然后再用移位运算02三种方法中的其中一种求出1的个数就可以了。
#include "stdio.h"
int main() {
int m,n;
scanf("%d %d", &m,&n);
int num = m ^ n;
int cut=0;
while (num) {
cut++;
num = num & (num -1);
}
printf("cut=%d\n",cut);
return 0;
}
03-打印整数的奇偶二进制位
编写代码实现:获取一个整数二进制序列中所有的偶数位和奇数位,分别打印出二进制序列。
思路分析:对于打印一个整数的二进制位,可以也使用移位操作符和位操作符,然后配合循环来实现;但是这里需要注意的是:屏幕上先打印的应该是二进制中的高位,这样才符合我们阅读数字的习惯,所以循环变量的初始值要设置为高。
int main() {
int m;
scanf("%d", &m);
for (int i = 31; i >=1 ; i-=2) { // 奇
printf("%d ", (m >> i) & 1);
}
printf("\n");
for (int i = 30; i >= 0; i-=2) { // 偶
printf("%d ", (m >> i) & 1);
}
printf("\n");
return 0;
}