CC55.【C++ Cont】位运算的最终总结(含比特位计数、汉明距离)

目录

1.知识回顾

2.总结

基础位运算

位运算的优先级

第i位是0还是1

修改第i位为1

修改第i位为0

位图的思想

提取最右侧的1(lowbit)

将最右侧的1变成0

异或操作运算律

3.练习

比特位计数

方法1:每个数都循环32次

方法2:相邻奇偶的1的个数规律

方法3:统计将最右侧的1变成0的次数(快速)

方法4:分范围讨论(暂时不讲)

汉明距离

分析


1.知识回顾

CC22.【C++ Cont】位运算总结(1)(例题五种解法!含汇编解法

CC23.【C++ Cont】位运算总结(2)

2.总结

基础位运算

>> << & | ! ~运算符的用法参见14.【C语言】初识操作符 上15.【C语言】初识操作符 下文章

位运算的优先级

不记优先级,直接加括号

第i位是0还是1

设二进制数x的最低位下标为0,必须先将第i位右移到最右侧,再做处理

第i位为 (x>>i) & 1

修改第i位为1

x = (1<<i) | x; //即x |= 1<<i;

修改第i位为0

x = (~(1<<i)) & x; //即x &= ~(1<<i);

位图的思想

这里简要提一提,位图(bitmap)用比特位存储信息,其本质是哈希表

例如int类型变量有4个字节,一共32个比特位,每一个位有0和1两种状态,可以存储信息

上述提到的三种方法做题会频繁用到,可以查看和修改位图

提取最右侧的1(lowbit)

n & (-n)

-n是按位取反再+1,n&(-n)的本质是将最右边的1左边的区域全部变成相反数

将最右侧的1变成0

n & (n-1)

n & (n-1)的本质是将最右边的1及其右边的区域全部变成相反数,因为n-1是要借位的

异或操作运算律

 参见E23.【C语言】练习:不创建第三个变量实现两个整数的交换文章

归零定律:a XOR a == 0

恒等定律:a XOR 0 == a

交换定律:a XOR b == b XOR a

自反定律:a XOR b XOR a == b

结合定律:a XOR b XOR c == a XOR (b XOR c) == (a XOR b) XOR c

3.练习

比特位计数

https://leetcode.cn/problems/counting-bits/description/

给你一个整数 n ,对于 0 <= i <= n 中的每个 i ,计算其二进制表示中 1 的个数 ,返回一个长度为 n + 1 的数组 ans 作为答案。

示例 1:

输入:n = 2
输出:[0,1,1]
解释:
0 --> 0
1 --> 1
2 --> 10

示例 2:

输入:n = 5
输出:[0,1,1,2,1,2]
解释:
0 --> 0
1 --> 1
2 --> 10
3 --> 11
4 --> 100
5 --> 101

提示:

  • 0 <= n <= 10^5

进阶:

  • 很容易就能实现时间复杂度为 O(n log n) 的解决方案,你可以在线性时间复杂度 O(n) 内用一趟扫描解决此问题吗?
  • 你能不使用任何内置函数解决此问题吗?(如,C++ 中的 __builtin_popcount

方法1:每个数都循环32次

由于n在int范围内,因此对于0~n的所有数,使用第i位是0还是1的方法,int一共32比特,因此循环32次,时间复杂度为O(N)

class Solution {
public:
    vector<int> countBits(int n) 
    {
       vector<int> ret; 
        for (int i=0;i<=n;i++)
        {
            //int 32bit
            int tmp=0;
            for (int j=0;j<32;j++)
            {
                tmp+=(i>>j)&1;
            }
            ret.push_back(tmp);
        }
        return ret;
    }
};

提交结果:

方法2:相邻奇偶的1的个数规律

从LeetCode的提示看:

相邻的奇数和偶数的1的个数其实是有规律的,设n(x)代表十进制x的二进制表示中1的个数,那么有

n(0)+1==n(1)

n(2)+1==n(3)

n(4)+1==n(5)

n(6)+1==n(7)

......

显然对于相邻的奇数和偶数,奇数的1的个数+1==偶数的1的个数,因此方法1可以优化

class Solution {
public:
    vector<int> countBits(int n)
    {
        vector<int> ret;
        for (int i = 0; i <= n; i += 2)
        {
            int tmp = 0;
            for (int j = 0; j < 32; j++)
            {
                tmp += (i >> j) & 1;
            }
            ret.push_back(tmp);
            ret.push_back(tmp + 1);
        }
        if (n % 2 == 0)
            ret.pop_back();
        return ret;
    }
};

 提交结果:

方法3:统计将最右侧的1变成0的次数(快速)

对于数x,每次将其最右侧的1变成0,直到x为0就停止操作,显然此方法比方法1快,不用每个数都循环32次

class Solution {
public:
    vector<int> countBits(int n)
    {
        vector<int> ret;
        for (int i=0; i<=n; i++)
        {
            int tmp=i;
            int num=0;
            while(tmp)
            {
                tmp&=tmp-1;
                num++;
            }
            ret.push_back(num);
        }
        return ret;
    }
};

当然方法3可以改进方法2,这里省略不写 

提交结果:

方法4:分范围讨论(暂时不讲)

从LeetCode的提示看:

 尝试分成[2-3],[4-7],[8-15]这几段看看规律

{1,2}、{1,2,2,3}、{1,2,2,3,2,3,3,4}、......有规律

后面的区间依靠前面的区间

这个解法需要用到动态规划,后面再说

汉明距离

https://leetcode.cn/problems/hamming-distance/description/

两个整数之间的 汉明距离 指的是这两个数字对应二进制位不同的位置的数目。

给你两个整数 xy,计算并返回它们之间的汉明距离。

示例 1:

输入:x = 1, y = 4
输出:2
解释:
1   (0 0 0 1)
4   (0 1 0 0)
       ↑   ↑
上面的箭头指出了对应二进制位不同的位置。

示例 2:

输入:x = 3, y = 1
输出:1

提示:

  • 0 <= x, y <= 231 - 1

注意:本题与 2220. 转换数字的最少位翻转次数 相同。

分析

先将x和y异或得到tmp,之后,使用将最右侧的1变成0的方法来统计tmp中1的个数sum,sum的值就是正确答案

class Solution {
public:
    int hammingDistance(int x, int y) 
    {
        int tmp=x^y;
        int sum=0; 
        while (tmp)
        {
            tmp&=tmp-1;
            sum++;
        }
        return sum;
    }
};

提交结果:

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

zhangcoder

赠人玫瑰手有余香,感谢支持~

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

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

打赏作者

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

抵扣说明:

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

余额充值