一.题目:
. - 力扣(LeetCode)
二.我的原始解法-一次性通过的python内置bin函数解法:
思路和题目描述一致,就是把0-n的每个数字转为二进制,计算这个二进制中1的个数添加到返回数组中,掌握基础函数即可。
class Solution:
def countBits(self, n: int) -> List[int]:
# bin解法
#技巧1:python中十进制转二进制是bin(x),返回0b开头的二进制字符串
#技巧2:python中计算字符串中某个子串的个数函数为s.count(substring),返回Int值
result = []
for x in range(0,n+1):
result.append(bin(x).count('1'))
return result
三.其他人的正确及好的解法,力扣解法参考:
这道题除了上面的bin()函数解法,还有一种动态规划解法+二进制数中1个数规律。
(1)动态规划理论基础可以看B站:
从此再也不怕动态规划了,动态规划解题方法论大曝光 !| 理论基础 |力扣刷题总结| 动态规划入门_哔哩哔哩_bilibili、10分钟彻底搞懂“动态规划”算法_哔哩哔哩_bilibili
(2)python的位运算符基础(位运算符其实就是二进制运算符号,区别于常用的十进制的加减乘除等符号)讲解:https://zhuanlan.zhihu.com/p/370167569、python 位运算之右移,左移,以及在数值的整数次计算中的应用_整数右移一位-CSDN博客
(3)二进制奇数偶数规律:Leetcode之动态规划(DP)专题-338. 比特位计数(Counting Bits) - 秦羽纶 - 博客园
(4)力扣上动态规划+位运算+二进制奇偶数易懂解法:
. - 力扣(LeetCode)
四.对于别人解法的消化及总结:
从0到n(n是一个非负整数,即0或正整数)的一个序列,如n为5时,这个序列为[0,1,2,3,4,5],这个序列是十进制的序列,要探究这个序列中每个十进制数的对应的二进制中1的个数,就先将这个序列转换为二进制序列,为[0,01,10,11,100,101],可以发现这当然也是一个递增的二进制序列,那么题目就转化为要探究这个二进制序列的1的个数规律。
这个二进制序列中1的个数的规律要分为奇数和偶数两个情况看(二进制的奇数也是十进制的奇数,二进制的偶数也是十进制的偶数),因为最低位2的0次方等于1。可以发现奇数的1的个数等于上一个偶数+1,偶数的1的个数等于这个偶数整除2的二进制数的1的个数,如这个二进制序列的1的个数为[0,1,1,2,1,2],L[0]=0,L[1]=1=L[0]+1,L[2]=L[2//2]=L[1]=1,L[3]=L[2]+1=3,L[4]=L[4//2]=L[2]=1,L[5]=L[4]+1=2。可以看到这个序列都是通过L[0]的1的个数和奇偶性计算出来的,那么就可以在代码中判断二进制的奇偶性,也就是对应n的奇偶性,也就是下标的奇偶性进行递归运算即可。由于这里计算的连续性,每个结果都是通过前面结果组合得到的,就可以通过动态规划保存前面结果的状态来减少递归次数,最终用动态规划来解决。动态规划需要确定dp数组,这里dp数组就是返回的结果数组,即[0,1,1,2,1,2],初始条件就是L[0]的值,下标代表的是n,也代表n的奇偶性(二进制及十进制的)。
总结一下:从十进制序列[0,1,2,3,4,5]到二进制序列[0,01,10,11,100,101]到二进制的1的个数序列[0,1,1,2,1,2],发现这个序列的变化规律为L[0]=0,L[1]=1=L[0]+1,L[2]=L[2//2]=L[1]=1,L[3]=L[2]+1=3,L[4]=L[4//2]=L[2]=1,L[5]=L[4]+1=2,发现这是一个动态规划问题,确认dp数组为这个序列,确认初始依赖值为L[0]=0。另外其实根据位运算原理就知道十进制偶数整除2就相当于每一位右移,而右移是不改变这个十进制数对应的二进制数的1的个数的,这个是二进制偶数1的变化规律的原理。所以由初始偶数知道初始奇数,就可以根据奇偶变化计算后面的各个数的1的个数了。
本题编程技巧:
(1)python中十进制转二进制是bin(x),返回0b开头的二进制字符串
(2)python中计算字符串中某个子串的个数函数为s.count(substring),返回Int值
(3)python中的位运算就是二进制运算符,主要有左移/右移/按位与/按位或/按位异或/按位取反等,用这些运算符算出来的都是对应的二进制数,具体运算符参考上面【三】中链接,这些二进制运算符其实可以等价于相应的十进制运算符,如左移<<,每左移一位得到的二进制数,如果转换成十进制数,就等于原二级制数对应的十进制数乘以2,也就是二进制左移一位=十进制*2;右移>>,每右移一位得到的二进制数,如果转换成十进制数,就等于原二进制对应的十进制数整除2,也就是二进制右移一位=十进制//2。所以可以利用这种对应关系,处理十进制的乘除等,也可以方便二进制转十进制。
(4)十进制数转换为二进制数,奇偶性不变
(5)二进制奇数等于前一个偶数在最低位+1,二进制偶数相当于这个偶数所在非负整数序列的下标//2的偶数右移一位,且右移不改变这个二进制数的1的个数
(6)递归解法要看下是否能用动态规划解决
bin解法通过截图:
动态规划+二进制1个数规律解法通过截图: