剑指offer系列-面试题43-1~n整数中1出现的次数(python)

1. 题目

输入一个整数n,求1~n这n个数的十进制表示中1出现的次数。例如,输入12, 1~12这些整数中包含1的数字有1, 10, 11和12, 1 一共出现了5次。

2. 解题思路

我的思路

思路1:

第一步:要找到一个数字中有几个1,可以通过求余数,余数为1则有一个1,再求除以10之后的商的余数,依次类推,直到数字变成零。统计余数为1的个数。
第二部:求1~n的整数中1的个数,就遍历地进行第一步,累加次数。

思路2:

思路2是思路1的循环实现

思路3:
将数字转成字符串再操作,毕竟对字符串的处理方式要比数字多得多。


书中的思路

  1. 针对一个数字21345,分析数字1在 1~ 21345中出现的次数存在何种规律;
  2. 将此规律转化成代码;
  3. 考虑各种边界情况。

3. 代码实现

3.1 思路1(递归)

时间复杂度O(nlogn),空间复杂度S(1)。这种的时间复杂度我不太清除咋算。。。。。


# 必须存为可变对象,因为如果存为数字那么是传给函数的只是一个值的副本,并不会修改这个全局变量。
g_count = {'count': 0}

def number_of_between_1_and_n():
	"""
	时间复杂度O(nlogn),空间复杂度S(1)
	"""
	for i in range(1, n+1):
		number_of_1(i, g_count)

def number_of_1(n, gcount):
    """
    n这个数字中出现1的次数
    思路1:递归,时间复杂度O(logn),空间复杂度S(n)
    :param n: 输入的整数
    """
    # 边界条件
    if n <= 0 or not isinstance(n, int):
        return 
    b = n % 10 # 获得余数,若余数为1,则次数加1
    if b == 1:
        gcount['count'] += 1
    # 获得商
    a = n // 10 # python3 需要用两个除号,否则得到的结果是float
    number_of_1(a, gcount)


if __name__ == '__main__':
    number_of_between_1_and_n(12, g_count)
    print(g_count)

3.2 思路2(循环)

时间复杂度O(nlogn),空间复杂度S(1)。这种的时间复杂度我不太清除咋算。。。。。


def number_of_between_1_and_n():
	"""
	时间复杂度O(nlogn),空间复杂度S(1)
	"""
	number = 0
	for i in range(1, n+1):
		number += number_of_1(i)
	return number

def number_of_1(n):
    """
    n这个数字中出现1的次数
    思路2:循环,时间复杂度O(logn),空间复杂度S(1)
    :param n: 输入的整数
    """
    num = 0
    while n > 0:
	    # 获得余数,若余数为1,则次数加1
	    if n % 10 == 1:
	        num += 1
	    n = n // 10
	return num
    
if __name__ == '__main__':
    res = number_of_between_1_and_n(12)
    print(res)

3.3 思路3

时间复杂度O(nlogn),空间复杂度S(1)。这种的时间复杂度我不太清除咋算。。。。。


def number_of_between_1_and_n():
	"""
	时间复杂度O(nlogn),空间复杂度S(1)
	"""
	number = 0
	for i in range(1, n+1):
		number += number_of_1(i)
	return number

def number_of_1(n):
    """
    n这个数字中出现1的次数
    思路3:将数字转成字符串来处理
    :param n: 输入的整数
    """
    num = 0
    for i in str(num):
    	if '1' == i:
    		num += 1
	return num
    
if __name__ == '__main__':
    res = number_of_between_1_and_n(12)
    print(res)

3.4 思路4

时间复杂度O(logn),空间复杂度S(n)。
首先,将整个功能分解为3个子功能。
尚未验证


def number_of_between_1_and_n(n):
	"""
	时间复杂度O(logn),空间复杂度S(1)
	:param n: 输入的整数
	"""
	# 若这个整数小于零或者根本就不是一个整数的话,直接返回0
	if n <= 0 or not isinstance(n, int):
		return 0
	
	return number_of_1(list(str(n)))

def number_of_1(ints):
    """
    因为python中没有指针,将数字转成list来处理
    :param ints: 字符串型数字的list
    """
    if not ints:
    	return 0
    # first是当前的最高位数字
    first = int(ints[0])
    length = len(ints) # 数字有多少位
    # 基线条件1,如果只有一位且数字是零,那么直接返回0
    if length == 1 and first == 0:
    	return 0
    # 基线条件2,如果只要1为且数字大于零,那么直接返回1
    if length == 1 and first > 0:
    	return 1
    
    # 假设是['2','1','3','4','5']
    # num_first_digit是数字10000~19999的第一位中的1的数目
    num_first_digit = 0
    # 如果最高位数字大于1,该当如何
    if first > 1:
    	# 获取除最高位之外的其它位中出现1的次数,如果数字为21345,那么最高位为1的数字,是10000~19999
    	num_first_digit = power_base_10(length-1)
    
    # 最高位数字等于1,该当如何
    elif first == 1:
    	num_first_digit = int(''.join(ints[1:])) + 1
    # 那么若是length > 1 且 first == 0 呢?例如20345,在传入['0', '3', '4', '5']
    # 那么num_first_digit肯定是零
    # num_other_digits是1346~21345除第一位之外的数位中的数目
    num_other_digits = first * (length - 1) * power_base_10(length -2)
    # num_recursive是1~1345中的数目
    num_recursive = number_of_1(ints[1:])

	return num_first_digit + num_other_digits + num_recursive

def power_base_10(n):
	"""
	获取除最高位之后的其它位中出现1的次数
	:param n: 去掉最高位之后,剩余的数字有几位
	去掉最高位之后,有几位,那么这一部分出现1的个数就是10的几次方
	"""
	result = 1
	for i in range(n):
		result *= 10
	return result
    
if __name__ == '__main__':
    res = number_of_between_1_and_n(12)
    print(res)

4. 总结

说实话,这道题太难我不会做。这个规律我是很难分析出来了,想起来脑壳疼。

5. 参考文献

[1] 剑指offer丛书

  • 4
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值