二进制提取位及各种运算
牛客链接
以上是求二进制的一道题,可以点击了解后再继续阅读
一.求"1"的个数
提供以下几种解题思路,并作比较
思路一:
循环进行以下操作,直到n被缩减为0:
- 用该数据模2,检测其是否能够被2整除
- 如果可以:则该数据对应二进制比特位的最低位一定是0,否则是1,如果是1给计数加1
- 如果n不等于0时,继续1
代码如下:
int count_one_bit(int n)
{
int count = 0;
while(n)//判断是否为0,即为终止条件
{
if(n%2==1)//判断最低位是否为1,奇偶思想
count++;//如果为1,计数器加一
n = n/2;//这就是让高位依次到最低位
}
return count;
}
上述方法缺陷:进行了大量的取模以及除法运算,取模和除法运算的效率本来就比较低。
思路二:
方法二思路:
一个int类型的数据,对应的二进制一共有32个比特位,可以采用位运算的方式
int count_one_bit(unsigned int n)
{
int count = 0;
int i = 0;
for(i=0; i<32; i++)
{
if(((n>>i)&1) == 1)
count++;
}
return count;
}
方法二优点:用位操作代替取模和除法运算,效率稍微比较高
缺陷:不论是什么数据,循环都要执行32次
再改进:
int forBinarynum(int n){
int result = 0;
while( n ){
result += (n & 1);
n = (n >> 1);
}
return result;
}
你以为这就结束了吗?不是的,以上的方法你只要在牛客网上实验,就会显示
思路3:
思路:采用相邻的两个数据进行按位与运算
举例:
9999:10 0111 0000 1111
第一次循环:n=9999 n=n&(n-1)=9999&9998= 9998
第二次循环:n=9998 n=n&(n-1)=9998&9997= 9996
第三次循环:n=9996 n=n&(n-1)=9996&9995= 9992
第四次循环:n=9992 n=n&(n-1)=9992&9991= 9984
第五次循环:n=9984 n=n&(n-1)=9984&9983= 9728
第六次循环:n=9728 n=n&(n-1)=9728&9727= 9216
第七次循环:n=9216 n=n&(n-1)=9216&9215= 8192
第八次循环:n=8192 n=n&(n-1)=8192&8191= 0
可以观察下:此种方式,数据的二进制比特位中有几个1,循环就循环几次,而且中间采用了位运算,处理起来比较高效
代码如下:
int count_one_bit(int n)
{
int count = 0;
while(n)
{
n = n&(n-1);
count++;
}
return count;
}
这个题解充分说明:造成两种结果,一种是通过不了,另一种是可以通过的.
原因是,要考虑的一个问题就是,关于1在高低位问题,关于高低位问题注意大小端,.点击可以详细了解.
在采用小端情况下,如果1几乎在低位,也就是大家习惯的二进制展开后左面部分,这就需要做很多次移位运算,所以考虑要全面,而对于无论1在高位还是低位,循环次数只取决于1的个数,具体原理上文已经解释过了.
如:0111 1111 1111 0000 0000 0000 0000 0000,前面几种方式需要移位,并且判断31次,而最后一种方法只需要移位运算且判断11次即可,一比较就很明显.
二.求两个整数二进制格式有多少个位不同
思路:
- 先将m和n进行按位异或,此时m和n相同的二进制比特位清零,不同的二进制比特位为1
- 统计异或完成后结果的二进制比特位中有多少个1即可
例子如下
代码如下:
#include<stdio.h>
int count_one_bit(int n)
{
int count = 0;
while(n)
{
n = n&(n-1);
count++;
}
return count;
}
int forFinalBinary(int m , int n){
int result = 0;
result = (m ^ n);
return result;
}
int main(){
int n = 0;
int m = 0;
scanf("%d %d",&n,&m);
printf("%d\n",count_one_bit(forFinalBinary(n,m)));
return 0;
}
运行结果如上,解决
三.打印整数二进制的奇数位和偶数位(获取一个整数二进制序列中所有的偶数位和奇数位,分别打印出二进制序列)
思路如下:
- 提取所有的奇数位,如果该位是1,输出1,是0则输出0
- 以同样的方式提取偶数位置
检测num中某一位是0还是1的方式:
- 将num向右移动i位
- 将移完位之后的结果与1按位与,如果:
结果是0,则第i个比特位是0
结果是非0,则第i个比特位是1
代码如下:
void Printbit(int num)
{
for(int i=31; i>=1; i-=2)
{
printf("%d ", (num>>i)&1);
}
printf("\n");
for(int i=30; i>=0; i-=2)
{
printf("%d ", (num>>i)&1);
}
printf("\n");
}
小结:
- 关于位运算的优点,题中详细体现了
- 就是关于运算思想的积累,如何了解最优解,再去学习,掌握,最优解不是一蹴而就的
- 本文没有详细介绍大小端存储,可以详细了解一下
不足之处欢迎指点,感谢阅读!