leetcode338.比特位计数

题目要求计算非负整数num在二进制表示下1的个数,并以数组形式返回。提供两种解题思路:一是通过观察规律分组求解,二是消除二进制末尾1并加1。目标是实现线性时间复杂度和线性空间复杂度的解决方案,避免使用内置计数函数。
摘要由CSDN通过智能技术生成

题目大意

给定一个非负整数 num。对于 0 ≤ i ≤ num 范围中的每个数字 i ,计算其二进制数中的 1 的数目并将它们作为数组返回。

示例 1:

输入: 2
输出: [0,1,1]

示例 2:

输入: 5
输出: [0,1,1,2,1,2]

进阶:

  • 给出时间复杂度为O(n*sizeof(integer))的解答非常容易。但你可以在线性时间O(n)内用一趟扫描做到吗?
  • 要求算法的空间复杂度为O(n)。
  • 你能进一步完善解法吗?要求在C++或任何其他语言中不使用任何内置函数(如 C++ 中的 __builtin_popcount)来执行此操作。

解题思路

方法一:

将每个数字的结果依次写出–> [0,||1,||1,2,||1,2,2,3,||1,2,2,3,2,3,3,4,||1,2,2,3,2…]。
我们可以将数字分组,每组的数量是2的幂(除了第一个数)。同时,每组数的前半部分是上一组数,每组数的后半部分是上一组数+1。这不难理解,6(110)可以看做是在2(10)的前面多放置了一个1,7(111)可以看做是在3(11)的前面多放了一个1.

class Solution {
public:
    vector<int> countBits(int num) {
    	if (n == 0)
    		return {0};
    	if (n == 1)
    		return {0, 1};

    	vector<int> res(num + 1, 0);
    	res[1] = 1;

    	num = num - 1;
		// 我们从第二组数开始计算,因此2^index是该组的长度,numIndex是起始位置
    	int index = 1, numIndex = 2;

    	while (num > 0){
    		// 计算当前组的长度,即2^index
    		int curTotalNums = 1 << index;
    		// 将该组分为前后两部分,前一部分等于前一组的结果,后一部分是前一组的结果+1
    		int half = curTotalNums / 2, left = numIndex - half;
    		// 如果该组所有数字都在给定范围内
    		if (curTotalNums <= num){
    			// 计算前半组
    			for (int i = 0; i < half; ++i){
    				res[numIndex++] = res[left++];
    			}
    			// 计算后半组
    			for (int i = 0; i < half; ++i){
    				res[numIndex++] = res[left++] + 1;
    			}
    			// 缩小范围
    			num -= curTotalNums;
    			// 下一轮的范围变大
    			++index;
    		}
    		// 如果当前组超出了给定范围,但是当前组的前半部分在范围内
    		else if (half <= num){
    			// 计算前半组
    			for (int i = 0; i < half; ++i){
    				res[numIndex++] = res[left++];
    			}
    			// 后半组就剩了有限个,即num-half个数字
    			for (int i = 0; i < num - half; ++i){
    				res[numIndex++] = res[left++] + 1;
    			}
    			break;
    		}
    		// 如果当前组超出范围,且前半部分也超出范围
    		else{
    			// 计算在范围内的数字
    			for (int i = 0; i < num; ++i){
    				res[numIndex++] = res[left++];
    			}
    			break;
    		}
    	}
    	return res;
    }
};
方法二:

前面说到过,6(110)可以看做是在2(10)的前面多放置了个1,7(111)是在3(11)的前面多放了个1。
换一种角度考虑,我们也可以认为6(110)是通过4(100)变的。即将4的二进制形式中的最后一个1后面的0变成1产生的。
因此我们可以先将某个数字num的二进制形式下最后一个1消掉,得到一个新的数字newnum,然后找到该数字的结果,在此结果上+1即可。
注意,因为我们将num中的最后一个1变成了0,因此newnum一定小于num。所以可以通过newnum得到num的结果。

class Solution {
public:
	vector<int> countBits(int num) {
		vector<int> res(num + 1, 0);
		for (int i = 1; i <= num; i++)
			// i & (i - 1)表示消掉i的最后一个1
			res[i] = res[i & (i - 1)] + 1;
		return res;
	}
};
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值