剑指offer 3.前n项数字二进制中1的个数(Brian Kernighan、动态规划、内置函数解法)

剑指offer 3.前n项数字二进制中1的个数

一、题目

描述:给定一个非负整数 n ,请计算 0 到 n 之间的每个数字的二进制表示中 1 的个数,并输出一个数组。
示例:
题目

二、题解

1. Brian Kernighan算法

基本思想:对于任意整数x,使用x & (x−1)方式可以将x的最后一个1变成0,对x重复进行该操作,直至x变成零。

class Solution:
    def countBits(self, n: int):
        def countOnes(x: int) :
            ones = 0
            while x > 0:
                x &= (x - 1)
                ones += 1
            return ones
        
        bits = [countOnes(i) for i in range(n + 1)]
        return bits

2. 动态规划

解题思路:动态规划。
奇数的二进制表示永远比前边的偶数的二进制表示多一个一。如果该数是奇数,则在之前的数量上加上一即可。

if i % 2 != 0:
    dp[i] = dp[i-1] + 1

偶数的二进制表示二进制的最低位(最右边的一位)一定是0,如果对之进行右移一位运算(即为把该数字除以二),他们中含有1的个数是一样的。比如 8 -> b’1000’ 和 4 -> b’100’, 12 -> b’1100’ 和 6 -> b’110’。如果该数是偶数,则直接将该数除以二。

if i % 2 == 0:
    dp[i] = dp[i >> 1]

初始化 0的二进制表示含有1的个数是0,1的二进制表示含有1的个数是在之基础上加以,而的二进制表示和除以二的二进制表示含有一的个数是相同的,以此类推可计算出所有n以内的数字二进制表示含有1的个数。因为要存储0-n的二进制表示含有1的个数,所以,dp存储数组的长度为n+1。

class Solution:
    def countBits(self,n: int):
        dp = [0 for _ in range(n + 1)]  # 初始化,具体可以看解题思路

        for i in range(1, n + 1):
            dp[i] = dp[i-1] + 1 if i % 2 != 0 else dp[i >> 1]   # 用三元表达式写更简洁一点
        return dp

3. python内置函数解法

class Solution(object):
    def countBits(self, n):
        """
        :type n: int
        :rtype: List[int]
        """
        number = []
        for i in range(n+1):
            num = len(bin(i).replace('0',''))-1#-1表示减去前缀b
            number.append(num)#没有初始化该位置的值只能用append
        return number

三、本道题遇到的知识盲区

1.bin(x)函数

bin(x)返回的是一个整数x的二进制字符串表示。其中,开头的“0b”表示是二进制
bin(1)=‘0b1’

bin(2)=‘0b10’

bin(3)=‘0b11’

bin(4)=‘0b100’

bin(5)=‘0b101’

2.二进制,八进制,十进制,十六进制概念与转换

2.1 数制的基本概念

数制: 也称为计数制,是一种计数的方法,是用一组固定的符号和统一的规则来表示数值的方法。在计数过程中采用进位的方法称为进位计数制(进制),包括数位、基数和位权三个要素。

  • 数位:指数字符号在一个数中所处的位置。
  • 基数:指在某种进位计数制中数位上所能使用的数字符号的个数。例如十进制的基数为10
  • 位权:数制中某一位上的1所表示数值的大小(所处位置的价值)。例如十进制的230,2的位权是100,3的位权是10,0的位权是1

2.2 进制的表示方式

  • 在计算机汇编语言中,常用的进制有二进制(Binary)、八进制(Octal)、和十进制(Hexadecimal)。其中,二进制是使用最广泛的,所有数字在计算机底层都是以二进制的形式存储的,且所有的数值,不管正负,底层都以补码的方式存储。
  • 进制的表示方式有两种:一种是数字右下角下表表示法:二进制数据 ( 11101010.010110100 ) 2 (11 101 010 . 010 110 100)_{2} (11101010.010110100)2对应八进制数据 ( 352.264 ) 8 (352.264)_{8} (352.264)8;另一种是后缀表示法:b(Binary)表示二进制数,o(Octal)表示八进制数,d(Decimal)或者不加后缀表示十进制数,h(Hexadecimal)表示十六进制数(若最高位为A~F,须在前面加上数字0,表示这是一个数值数据);还有一种是前缀表示法,0b(0B)表示二进制:‘0b1010’,0o表示八进制:‘0o12’(好像不加前缀也可以表示八进制?int(‘12’,8)=10),不加前缀表示十进制:‘10’,0x(0X)表示十六进制:‘0xa’。python中的进制表示默认使用前缀表示法。

2.3 python进制的转换方式

2.3.1十进制转换为其他进制

  • 方式一:函数
    • 十进制转换为二进制:bin()函数:bin(10) --> ‘0b1010’
    • 十进制转换为八进制:oct()函数:oct(10)–> ‘0o12’
    • 十进制转换为十六进制:hex()函数:hex(10) --> ‘0xa’
  • 方式二:format
    • 十进制转换为二进制:bin()函数:‘{:b}’.format(9)–>1001
    • 十进制转换为八进制:oct()函数:‘{😮}’.format(9)–>11
    • 十进制转换为十六进制:hex()函数:‘{:x}’.format(10)–>a

2.3.2 其他进制转换为十进制

  • 方式一:int()函数
    • 二进制转换为十进制:int(‘0b1010’,2) --> 10 或者 int(‘1010’,2) --> 10
    • 八进制转换为十进制:int(‘0o12’,8) --> 10 或者 int(‘12’,8) --> 10
    • 十六进制转换为十进制:int(‘0xa’,16) --> 10 或者 int(‘a’,16) --> 10
  • 方式二:eval()函数
    备注:type必须是str且前面的进制标志(0b,0o,0x)不可少
    • eval(‘1111’) -->1111
    • eval(‘0b1111’) -->15
    • eval(‘0o1111’) -->585
    • eval(‘0x1111’) -->4369

2.3.3 将十进制转换为固定长度的多进制类型

  • 方式一:format
    • 十转二长度为8:‘{:08b}’.format(9)
    • 十转八长度为6:‘{:06o}’.format(9)
    • 十转十六六长度为六:‘{:06x}’.format(9)
  • 方式二:
    a =bin(5)[2:] -->101
    c = str.zfill(a,8)–>00000101
    a =oct(9)[2:]–>11
    c = str.zfill(a,8)–>00000011
    a =hex(20)[2:]#去掉前面0b–>14
    a =hex(20)–>‘0x14’
    str.zfill(a,8)–>‘00000x14’
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

liqianjiqiren

你的鼓励将是我创作的最大动力

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

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

打赏作者

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

抵扣说明:

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

余额充值