【LeetCode刷题笔记-66 338:比特位计数】

题目:
在这里插入图片描述
这题其实方法上来说,有三种。

1.暴力法就不多说了,移位计算是否为1,然后统计次数输出即可。

2.用到了前两天计算的前缀和方法。
怎么想到这个解法呢。
先写一些二进制的数,你就可以观察。
比如
0 0
1 1
2 10
3 11
4 100
5 101
6 110
7 111
8 1000
以此类推

我们可以观察得出几个结论。
(1)2的指数数字处的1的个数为1
(2)每移位一次(倍增一次,2->4,4->8)才会多出一位
(3)相邻指数数字处中间1的个数可以由前面的数字相加得到。
重点解释一下第三点:
比如
3 = 2 + 1(这里的等式指的是1的数字个数)
6 = 4 + 2
7 = 4 + 3

会有这种性质是因为每一次倍增的时候,最高位是定死的,而只有后面的几位可以变动,而后面几位变动的值已经计算过了。
比如
7 111 = 1(最高位) + 11(后两位)= 100 + 011 = 4 + 3 =7

所以就可以得到一个等式。
我们用Flag表示上一个二的幂指数,NextFlag表示下一个二的幂指数
ans[i] = ans[flag] + ans[i-flag];
ans为答案数组,i为遍历下标
于是就可以得到这个算法了。

值得一提的是,这里需要初始化前两个数字,然后从2开始循环。
C++代码附带测试:

#include<iostream>
#include<vector>

using namespace std; 


class Solution {
public:
    vector<int> countBits(int num) {
		vector<int> ans(num+1);
		if(num==0)
		{
			return ans;
		}
		if(num==1)
		{
			ans[0] = 0;
			ans[1] = 1;
			return ans;
		}
		ans[0] = 0;
		ans[1] = 1;
		int flag = 1;
		int nextflag = 2;
		for(int i =2;i<=num;i++)
		{
			if(i!=nextflag)
			{
				ans[i] = ans[flag] + ans[i-flag];
			}
			else if(i==nextflag)
			{
				ans[i] = 1;
				flag <<=1;//左移一位 相当于乘2
				nextflag <<= 1;
			}
		}
		return ans;
    }
};


int main()
{
	int num = 5;
	Solution solution;
	vector<int> ans = solution.countBits(num);
	for(int i=0;i<ans.size();i++)
	{
		cout<<ans[i]<<"  ";
	}
}

3.位运算法
这个方法很神奇,没有了解过的话是不太能想到的。
这个算法的基础来自于这个式子:
n &= (n -1),可以直接清除最低位的1

为什么呢?
我们可以反过来想一想。

因为从二进制的角度讲,n相当于在n - 1的最低位加上1。举个例子,8(1000)= 7(0111)+ 1(0001),所以8 & 7 = (1000)&(0111)= 0(0000),清除了8最右边的1(其实就是最高位的1,因为8的二进制中只有一个1)。再比如7(0111)= 6(0110)+ 1(0001),所以7 & 6 = (0111)&(0110)= 6(0110),清除了7的二进制表示中最右边的1(也就是最低位的1)。

来自这个博客

所以,凭借这个式子,相当于可以优化暴力法。并且可以想到,假如一个二进制数里面有非常多的0,需要直接计算1的个数,这个办法相当好用。
C++代码(附带测试):

#include<iostream>
#include<vector>

using namespace std; 

//第一种方法 
//class Solution {
//public:
//    vector<int> countBits(int num) {
//		vector<int> ans(num+1);
//		if(num==0)
//		{
//			return ans;
//		}
//		if(num==1)
//		{
//			ans[0] = 0;
//			ans[1] = 1;
//			return ans;
//		}
//		ans[0] = 0;
//		ans[1] = 1;
//		int flag = 1;
//		int nextflag = 2;
//		for(int i =2;i<=num;i++)
//		{
//			if(i!=nextflag)
//			{
//				ans[i] = ans[flag] + ans[i-flag];
//			}
//			else if(i==nextflag)
//			{
//				ans[i] = 1;
//				flag <<=1;//左移一位 相当于乘2
//				nextflag <<= 1;
//			}
//		}
//		return ans;
//    }
//};

class Solution{
	public:
		int BitCount2(int n){
			int sum = 0;
			while(n!=0)
			{
				n &= (n-1);
				sum++;
			}
		    return sum;
		}	
		
		vector<int> countBits(int num){
			vector<int> ans(num+1);
			for(int i=0;i<=num;i++)
			{
				ans[i] = BitCount2(i);
			}
			return ans;
		}	
};


int main()
{
	int num = 5;
	Solution solution;
	vector<int> ans = solution.countBits(num);
	for(int i=0;i<ans.size();i++)
	{
		cout<<ans[i]<<"  ";
	}
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值