位运算(& | ~ ^ >> <<)

前言: 

&    与
|    或
~    非
^    异或(相同为0,相异为1)
>>    右移
<<    左移

 
常用操作:

求x的二进制位的第k位数字 x >> k & 1
lowbit(x) = x & -x,返回x的二进制位的最后一位1

目录

 二进制中1的个数

算法1、(暴力枚举) O(nlongn)

算法2、 (lowbit) O(nlogn)

算法3、(位运算) 接近O(1)

 整数转换​

-1的返回值


 二进制中1的个数

算法1、(暴力枚举) O(nlongn)

思路:

对于每个数字a,a&1得到了该数字的最后一位,之后将a右移一位,直到位0,就得到了1的个数

C++ 代码

#include<iostream>
using namespace std;
int n;
int a,k;
int main(){
    scanf("%d",&n);
    for(int i=0;i<n;i++){
        scanf("%d",&a);
        k=0;
        while(a){
            k+=a&1;
            a=a>>1;
        }
        printf("%d ",k);
    }
    return 0;
}

算法2、 (lowbit) O(nlogn)

思路:

使用lowbit操作,进行,每次lowbit操作截取一个数字最后一个1后面的所有位,每次减去lowbit得到的数字,直到数字减到0,就得到了最终1的个数;

根据计算机负数表示的特点,数字在内存中以补码形式存储,正数原码、反码、补码相同;负数表示形势是补码,就是反码+1,

如:10的补码00001010,-10的补码为反码+1,即11110110(11110101+1),二者按位与得到了00000010,就是2,10 - 2=8,8补码00001000.......一直减下去直到变成00000000

#include<iostream>
using namespace std;
int lowbit(int x){
    return x&(-x);
}
int main(){
    int n;
    cin>>n;
    while(n--){
        int x;
        cin>>x;

        int res=0;
        while(x) x-=lowbit(x),res++;

        cout<<res<<' ';
    }
    return 0;
}

算法3、(位运算) 接近O(1)

思路:

读懂了算法2的操作就明白了,算法2的优化

#include <iostream>
using namespace std;

int cnt(int b){
 int res = 0;
 while (b>0) {
    b = b & (b-1);
    res++;
 }
 return res;
}


int main(){
int n, b;
cin >> n;
for (int i=0;i<n;i++) {
    cin >> b;
    cout << cnt(b)<<" ";
}

return 0;
}

 整数转换

解题思路

  • n = A ^ B,可以得到A和B有哪几位是不同的,即n二进制1的个数,就是A和B有几位不同的数量
  • n = n & (n - 1)可以去掉n二进制的最右边的一个1,可以用来统计1的数量,那么1的数量就是答案
class Solution {
public:
    int convertInteger(int A, int B) {
        unsigned int  n = A ^ B;
        int cnt = 0;
        while(n){
            n &= n - 1;
            cnt++;
        }
        return cnt;
    }
};

 注意:

如果A,B每一位都不同,如 01010101... 和 10101010...,这样异或结果为32个1。 此时 x - 1会超过 INT_MIN,所以必须将x设置为无符号整数。(不可用long long,因为有64位,可能导致异或结果多出32)

-1的返回值

1 、求函数返回值,传入 - 1 ,则在 64 位机器上函数返回( )
int func(int x)
{
	int count = 0;
	while (x)
	{
		count++;
		x = x & (x - 1);//与运算 
	}return count;
}

A: 死循环         B: 64         C: 32         D: 16

正确答案: C
x=x&(x-1)这个表达式执行一次就会将x的2进制中最右边的1去掉,在x变成0之前,表达式能执行几次,就去掉几个1,所以
这个代码实现了求一个有符号整数二进制补码中1的个数的功能,我们知道-1的补码是全1,而int类型4个字节32位,选C

2 、读代码选结果( )
int count = 0; 
int x = -1; 
while (x) 
{ 
	count++; 
	x = x >> 1; 
}
printf("%d", count);

A: 1         B: 2         C: 32         D: 死循环,没结果

正确答案: D
此题一个关键,有符号数右移运算高位是补符号位的,负数的符号位是1,所以x永远不会变为0,是个死循环

3、下面函数的输出结果是( )
void func() 
{ 
	int k = 1 ^ (1 << 31 >> 31);
	printf("%d\n", k); 
}
A: 0         B: -1         C: -2         D: 1
正确答案: C
(1 << 31 );左移31位,并在右侧填充0,得到0x80000000,即符号位为1,其他为0即-2147483648
int k = 1^(1 << 31 >> 31);注意,这里在右移的时候,符号位保持为1,右移后填充1,结果为0xFFFFFFFF,即-1,
0x00000001^0xFFFFFFFF,即0xFFFFFFFE(-2)

  • 19
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 11
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 11
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

NO.-LL

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值