位运算是把数字用二进制表示之后,对每一位上0或者1的运算。二进制及其位运算是现代计算机学科的基石,很多底层的技术也都离不开位运算,由于在我们日常生活中习惯了十进制,很多人看到二进制以及位运算都感到很难适应,接下来我们一起讨论一下移位操作符和位操作符以及位运算。
移位操作符
<< 左移操作符
>> 右移操作符
位操作符
& 按位与(二进制位)
| 按位或(二进制位)
^ 按位异或 相同为0相异 相异为1(二进制位)
注意区分逻辑与 &&(两真为1) 和逻辑或 ||(两假为0)
我们来看一道面试题(360):
#include <stdio.h>
int main()
{
int i = 0,a = 0,b = 2,c = 3,d = 4;
i = a++ && ++b && d++;
//i = a++ || ++b || d++;
printf("a=%d b=%d c=%d d=%d",a,b,c,d);
return 0;
}
运行结果:
a=1 b=2 c=3 d=4
解析:
逻辑&&运算一旦左边为假后面便不再运算,本题a=0先发生作用使&&左边为假b,c,d的值不再变化,a再++变为1 输出。
位运算
其实二进制的位运算并不是很难掌握,因为位运算总共只有上述的五种,&,|,^,>>,<<。
注:下表的0 1均在二进制环境中
我们通过几个例子来熟悉掌握位运算吧!
题目:实现一个函数,输入一个整数,输出该数二进制表示中1的个数(剑指offer面试题)
解法一:(可能引起死循环的解法)
- 基本思路:先将这个数二进制表中最后一位与1做&运算判断最后一位的值,然后再将这个数右移一位即可判断倒数第二位的值,以此类推,定义计数器count,&的结果为1则count++,直到这个数为0跳出while循环,输出count。
int Count(int n)
{
int count = 0;
while (n)
{
if (n & 1)
count++;
n = n >> 1;
}
return count;
}
注意:虽然右移一位和÷2在数学上等价,但除法效率比右移操作低得多,所以不能更换右移操作为除法。
缺陷:如果输入负数,右移过程中高位始终补1,最终陷入死循环。
解法二:(常规解法)
- 基本思路:为了避免死循环可以不移动输入的n,去左移与n进行&运算的1,直到flag的二进制变为全0,跳出while,输出count。
int Count(int n)
{
int count = 0;
int flag = 1;
while (flag)
{
if (n & flag)
count++;
flag = flag << 1;
}
return count;
}
缺陷:不论1的个数为多少,该程序都会循环32次
解法三:(给面试官带来惊喜的解法)
- 基本思路:把一个整数n减去1,都是把它二进制序列最右边的1变为0,而左边的其他位保持不变,右边的0变为1。再将n和n-1做&运算,下一个1之前的二进制序列全都变为0。n中有几个1就进行几次&运算,大大提高了程序的效率。
int Count(int n)
{
int count = 0;
while (n)
{
n = n&(n - 1);//每次&都是最后一个为1的位变为0
count++;
}
return count;
}
亮点:有多少个1就循环多少次