面试题15-二进制中1的个数

题目:

请实现一个函数,输入一个整数,输出该数二进制表示中1的个数。例如,把9表示成二进制是1001,有2位是1。因此,如果输入9,则该函数输出2。

解题思路

如果一个整数不为0,那么这个整数至少有一位是1。如果我们把这个整数减1,那么原来处在整数最右边的1就会变为0,原来在1后面的所有的0都会变成1(如果最右边的1后面还有0的话)。其余所有位将不会受到影响。

举个例子:一个二进制数1100,从右边数起第三位是处于最右边的一个1。减去1后,第三位变成0,它后面的两位0变成了1,而前面的1保持不变,因此得到的结果是1011.我们发现减1的结果是把最右边的一个1开始的所有位都取反了。这个时候如果我们再把原来的整数和减去1之后的结果做与运算,从原来整数最右边一个1那一位开始所有位都会变成0。如1100&1011=1000.也就是说,把一个整数减去1,再和原整数做与运算,会把该整数最右边一个1变成0.那么一个整数的二进制有多少个1,就可以进行多少次这样的操作。

C++实现

int NumberOf3(int n)
{
	int count = 0;
	while (n)
	{
		++count;
		n = (n - 1)&n;
	}
	return count;
}
  1. 时间复杂度:O(logn),最坏情况下n的二进制位全部为1
  2. 空间复杂度:O(1)

Python实现

class Solution:
    def NumberOf1(self,n):
        return sum([(n>>i&1) for i in range(0,32)])
  1. 时间复杂度:O(k),其中k是int型的二进制数,k=32

测试用例

  1. 正数(包括边界值1、0x7FFFFFFF);
  2. 负数(包括边界值0x80000000、0xFFFFFFFF);
  3. 0

338-比特位计数

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

方法一:Brian Kernighan 算法

最直观的做法是对从 0到 n 的每个整数直接计算「一比特数」。每个 int 型的数都可以用 32 位二进制数表示,只要遍历其二进制表示的每一位即可得到 1 的数目。

利用 Brian Kernighan 算法,可以在一定程度上进一步提升计算速度。Brian Kernighan 算法的原理是:对于任意整数 x,令x=x & (x−1),该运算将 x 的二进制表示的最后一个 1 变成 0。因此,对 x 重复该操作,直到 x 变成 0,则操作次数即为 x 的「一比特数」。

对于给定的 n,计算从 0 到 n 的每个整数的「一比特数」的时间都不会超过 O(logn),因此总时间复杂度为 O(nlogn)。

class Solution {
public:
    int countOnes(int x) {
        int ones = 0;
        while (x > 0) {
            x &= (x - 1);
            ones++;
        }
        return ones;
    }
    vector<int> countBits(int n) {
        vector<int> bits(n + 1);
        for (int i = 0; i <= n; i++) {
            bits[i] = countOnes(i);
        }
        return bits;
    }
};
  1. 时间复杂度:O(nlogn)。需要对从 0 到 n 的每个整数使用计算「一比特数」,对于每个整数计算「一比特数」的时间都不会超过 O(logn)。
  2. 空间复杂度:O(n)

方法二:动态规划——最高有效位

方法一需要对每个整数使用 O(logn) 的时间计算「一比特数」。可以换一个思路,当计算 i 的「一比特数」时,如果存在 0≤j<i,j 的「一比特数」已知,且 i 和 j 相比,i 的二进制表示只多了一个 1,则可以快速得到 i 的「一比特数」。

令bits[i] 表示 i 的「一比特数」,则上述关系可以表示成bits[i]=bits[j]+1。

对于正整数 x,如果可以知道最大的正整数 y,使得 y≤x 且 y 是 2 的整数次幂,则 y 的二进制表示中只有最高位是 1,其余都是 0,此时称 y 为 x 的「最高有效位」。令 z=x−y,显然 0≤z<x,则 bits[x]=bits[z]+1。

为了判断一个正整数是不是 2 的整数次幂,可以利用方法一中提到的按位与运算的性质。如果正整数 y 是 2 的整数次幂,则 y 的二进制表示中只有最高位是 1,其余都是 0,因此y & (y−1)=0。由此可见,正整数 y 是 2 的整数次幂,当且仅当y & (y−1)=0。

显然,0 的「一比特数」为 0。使用highBit 表示当前的最高有效位,遍历从 1 到 n 的每个正整数 i,进行如下操作。

如果i & (i−1)=0,则令highBit=i,更新当前的最高有效位。

i 比 i−highBit 的「一比特数」多 1,由于是从小到大遍历每个整数,因此遍历到 i 时,i−highBit 的「一比特数」已知,令bits[i]=bits[i−highBit]+1。

最终得到的数组 bits 即为答案。

class Solution {
public:
    vector<int> countBits(int n) {
        vector<int> bits(n + 1);
        int highBit = 0;
        for (int i = 1; i <= n; i++) {
            if ((i & (i - 1)) == 0) {
                highBit = i;
            }
            bits[i] = bits[i - highBit] + 1;
        }
        return bits;
    }
};
  1. 时间复杂度:O(n)。对于每个整数,只需要 O(1) 的时间计算「一比特数」
  2. 空间复杂度:O(1)。除了返回的数组以外,空间复杂度为常数

方法三:动态规划-最低有效位

方法二需要实时维护最高有效位,当遍历到的数是 2 的整数次幂时,需要更新最高有效位。如果再换一个思路,可以使用「最低有效位」计算「一比特数」。

对于正整数 x,将其二进制表示右移一位,等价于将其二进制表示的最低位去掉,得到的数是 x/2。如果bits[x/2] 的值已知,则可以得到bits[x] 的值:

如果 x 是偶数,则bits[x]=bits[x/2] ;

如果 x 是奇数,则bits[x]=bits[x/2] +1。

上述两种情况可以合并成:bits[x] 的值等于 bits[x/2]的值加上 x 除以 2 的余数。

由于x/2可以通过x>>1 得到,x 除以 2 的余数可以通过x & 1 得到,因此有:bits[x]=bits[x>>1]+(x & 1)。

遍历从 1 到 n 的每个正整数 i,计算bits 的值。最终得到的数组 bits 即为答案。

class Solution {
public:
    vector<int> countBits(int n) {
        vector<int> bits(n + 1);
        for (int i = 1; i <= n; i++) {
            bits[i] = bits[i >> 1] + (i & 1);
        }
        return bits;
    }
};
  1. 时间复杂度:O(n)。对于每个整数,只需要 O(1) 的时间计算「一比特数」
  2. 空间复杂度:O(1)。除了返回的数组以外,空间复杂度为常数

方法四:动态规划——最低设置位

子问题设置为y=x&(x-1),动态转移方程为:bits[y]=bits[x&(x-1)]+1

class Solution {
public:
    vector<int> countBits(int n) {
        vector<int> bits(n + 1);
        for (int i = 1; i <= n; i++) {
            bits[i] = bits[i & (i - 1)] + 1;
        }
        return bits;
    }
};
  1. 时间复杂度:O(n)。对于每个整数,只需要 O(1) 的时间计算「一比特数」
  2. 空间复杂度:O(1)。除了返回的数组以外,空间复杂度为常数。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值