位运算习题题解

在这里插入图片描述

今天学习的内容是位运算,位运算就是使用位操作符号操作二进制位。

按位与&

任何二进制位&上1都是本身,&0都是0,所以可以用来消除一个数的后五位,或者保留后五位,&可以用来判断奇偶性,因为偶数的二进制位最低为是0,奇数的最低位是1

按位与还可以用来计算一个数的二进制有多少个1,n&=(n-1),每次都会把n最低位的1消除,消除了几次最后变为全零,这个数就有几个1.

按位或 |

按位比较同位0,就是0,有一个是1,按位或完就是1,任何数或上0,都是本身。

按位异或^

相同为0,不同为1.

两个相同的数,异或完之后是0,任何数异或上0,都是本身。

位移运算符>>(右移位数) and <<(左移位数)

右移相当于除2,左移相当于乘2
右移高位补上符号位,左移低位补零高位直接丢弃。

题解

868. 二进制间距

思路:用循环遍历出每一位二进制位,然后如果这一个数的某一个二进制位第一次为1,那就先把它赋值给right,所以right初始值为-1,就是为了只给right首次的第一次赋值执行一次并且不计算长度,因为如果right为0,数字二进制位中只有一个1的话,就会计算错误。然后第二次遇到1的时候就计算间距,并且更新right

int binaryGap(int n){
    int ans = 0;
    int right = -1;
    for(int i=0;i<32;i++)
    {
        if((n>>i)&1)
        {
            if(right==-1)
                right = i;
            else
            {
                ans = ans>(i-right)?ans:(i-right);
                right = i;
            }
        }
    }
    return ans;
}

1734. 解码异或后的排列

首先题目条件,perm是n个正整数的排列,也即是,perm里面的n个数字就是从1到n的乱序。
而观察en数组,可以发现en[1]en[3]…en[n-2],最后相当于perm数组从1异或到n-1,此时我们将1到n异或到一起,然后再异或上上面的perm从1 到n-1的异或就得到了perm[0],然后迭代求出来后面的perm数组元素即可。

int* decode(int* encoded, int encodedSize, int* returnSize){
    int*ans = (int*)malloc(sizeof(int)*(encodedSize+1));
    *returnSize = encodedSize+1;
    int ret = 0;
    int sum = 0;
    for(int i =1;i<=encodedSize+1;i++)
    {
        ret^=i;
    }
    for(int i =1;i<encodedSize;i+=2)
    {
        sum^=encoded[i];
    }
    ans[0] = sum^ret;
    for(int i =1;i<encodedSize+1;i++)
    {
        ans[i] = ans[i-1]^encoded[i-1];
    }
    return ans;
}

89. 格雷编码

格雷码,n位格雷码,就是n位二进制位,对应数字范围是0–(2^n)-1,所以只需要将这些数字转换成对应的格雷码即可,格雷码的第i位是二进制的第i位异或上第i+1位。迭代求出来就行了。

int* grayCode(int n, int* returnSize){
    int size = 1<<n;
    int* ans = (int*)malloc(sizeof(int)*size);
    for(int i =0;i<size;i++)
    {
        ans[i] = (i>>1)^i;
    }
    *returnSize = size;
    return ans;
}

1238. 循环码排列

先得到格雷码,因为格雷码得第一位肯定是0,所以异或上start就是start,原本start得位置异或上start就变成了0,格雷码是循环的所以就完成了循环码的构建。

int* circularPermutation(int n, int start, int* returnSize){
    int size = 1<<n;
    int*ans = (int*)malloc(sizeof(int)*size);
    for(int i=0;i<size;i++)
    {
        ans[i] = (i>>1)^i^start;
    }
    *returnSize = size;
    return ans;
}

补充(五月)

191. 位1的个数

直接按位与,每次可以消除一个二进制位中的1,所以计数器++。

int hammingWeight(uint32_t n) {
    int ans = 0;
    while(n)
    {
        n&=(n-1);
        ans++;
    }
    return ans;
}

461. 汉明距离

汉明距离就是计算这两个数字二进制位对应不相同的个数,先将连个数字异或起来,然后计算这个异或后的数字有几个1,就可以了

int hammingDistance(int x, int y){
    int ans = 0;
    int c = x^y;
    while(c)
    {
        c &= (c-1);
        ans++;
    }
    return ans;
}

136. 只出现一次的数字

相同数字异或之后是0,只要将所有数字异或到一起,剩下的那个数字就是只出现过一次的数字。

int singleNumber(int* nums, int numsSize){
    int ans = 0;
    for(int i =0;i<numsSize;i++)
    {
        ans^=nums[i];
    }
    return ans;
}

137. 只出现一次的数字 II

思路:出现3次的数字,二进制位必当是3的倍数,只要将数组元素的每一位取出来,加到一起,如果是3的倍数则不管,不是3的倍数,说明那个出现了1次的数字的这个二进制位是1,将1左移i位加到ret上,要注意这里左移的1要是无符号数,不然有符号int左移31位会报错。

要注意,有符号1不能左移31位,因为符号位被修改也就是符号位原来那个0,被移出去了,符号位的移入和移出都是未定义的行为,所以会报错。这里如果想要得到1左移31位的那个值就需要换成更大的类型,或者用无符号去掉符号位,下面那个强制转换,两种写法都可以。

int singleNumber(int* nums, int numsSize){
    int ans = 0;
    int ret = 0;
    for(int i =0;i<32;i++)
    {
        ans = 0;
        for(int j = 0;j<numsSize;j++)
        {
            ans+= (nums[j]>>i)&1;
        }
        if(ans%3!=0)
        {
            //ret+=((long long)1<<i);
            ret+=((unsigned int)1<<i);
        }
    }
    return ret;
}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-bNlbvNwl-1655026325313)(int左移31位报错问题.png)]

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

KissKernel

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

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

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

打赏作者

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

抵扣说明:

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

余额充值