【google面试题】求1到n的正数中1出现的次数的两种思路及其复杂度分析

本文详细介绍了解决从1到n整数中1出现次数的问题,包括两种思路和代码实现。通过分析,提出了一般化的公式来计算任意位数的数中1出现的次数,简化了计算过程。该题是广受关注的Google面试题之一。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

问题描述:

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

这是一道广为流传的google面试题。

算法:

第一种思路:对从1到n的每个数进行统计,统计的结果相加。算法复杂度为O(n)

第二种思路:举例说明。令n=321,则:

它的个位上出现1的形式为XY1,次数和为33(因为十位百位上可能出现的组合为0-32)

它的十位上出现1的次数和较为复杂,形式为X1Y,X的取值范围为0-3,Y的取值范围为0-9,所以X1Y一共有40中形式,即1在十位出现次数和为40

它的百位上出现1的形式为1XY显然,XY的取值范围为00-99,所以次数和为100

故总和为100+40+33=173

经过分析,我们可以的到一个一般化的公式:

一个digit位的数n,从右数第i位(i从0开始)上出现1的次数和为:(i+1位到digit-1位的组合数+1)*10的i次方

数学表达为

count(i)=[n/pow(10,i+1)+1]*pow(10,i)

sum=∑count(i)

由于位数digit=[logn]+1   (其中logn为向下取整),所以时间复杂度为O(logn)

代码实现:

第一种思路:

#pragma once
#include<iostream>
using namespace std;

int Find(int m)
{
	int sum = 0;
	while (m > 0)
	{
		int temp;
		temp = m % 10;
		if (temp == 1)
			sum += 1;
		m = m / 10;
	}
	return sum;
}

int Count(int n)
{
	int sum = 0;
	for (int i = 1; i <= n; i++)
		sum += Find(i);
	return sum;
}

void  main()
{
	int n;
	cin >> n;
	Count(n);
	cout << Count(n) << endl;
	system("pause");
}

第二种思路:

#pragma once
#include<iostream>
#include"math.h"
using namespace std;

int Count(int list[], int n,int digit)
{
	int sum = 0;
	for (int i = 0; i < digit; i++)
		sum += (int(n / pow(10, i + 1)) + 1)*(int)pow(10, i);
	return sum;
}

void  main()
{
	int n;
	cin >> n;
	int temp = n;
	int digit = 1;//n的位数
	while (temp > 0)
	{
		if (temp - temp % 10 > 0)
			digit++;
		temp = temp / 10;
	}
	int* list = new int[digit];//list[i]中是n从右边第i位上的数字
	for (int i = 0; i < digit; i++)
		list[i] = (n - int(n%(int)pow(10, i))) / (int)pow(10, i) % 10;
	cout << Count(list, n,digit) << endl;
	system("pause");
}


评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值