题目描述:输入一个整数n,求从1到n这n个整数的十进制表示中1出现的次数。例如输入12,从1到12这些整数中它含1的数字有1,10,11和12,1一共出现了5次。
解题思路:
- 解法一:循环依次对每一个1~n之间的数求模,若模为1则计次,最终返回1出现的次数。复杂度高O(n*longN).
解法二:从数字规律着手明显提高时间效率的解法O(logn)
测试用例:
int main(){
//输入数组
int number = 12;
//结果
int res = NumberOf1Between1AndN(number);
std::cout << "Number of one in N is: " << res;
return 0;
}
函数实现:
//辅助函数
int PowerBase10(unsigned int n){
int result = 1;
for(unsigned int i = 0; i < n; ++i)
result *= 10;
return result;
}
//函数实现,利用字符串方便编程
//核心函数
int NumberOf1(const char *strN){
if(!strN || *strN < '0' || *strN > '9' || *strN == '\0')
return 0;
int first = *strN - '0'; //第一位
unsigned int length = static_cast<unsigned int>(strlen(strN)); //字符串长度
if(length == 1 && first == 0)
return 0; //如果长度为1 && 第一位等于0,那么1出现的次数必然为0
if(length == 1 && first > 0)
return 1; //如果长度为1 && 第一位大于0, 那么1必然出现1次
//假设strN是“21345”
//numFirstDigit是数字10000~19999的第一个位中的数目
int numFirstDigit = 0;
if(first > 1)
numFirstDigit = PowerBase10(length - 1); //直接求出共有多少个1
else if(first == 1)
numFirstDigit = atoi(strN + 1) + 1; //直接转化为次数
//numOtherDigits是1346~21345除了第一位之外的数位中的数目
int numberOtherDigits = first * (length - 1) * PowerBase10(length - 2);
//numRecursive是1~1345中的数目
int numRecursive = NumberOf1(strN + 1);
return numFirstDigit + numberOtherDigits + numRecursive;
}
//接口函数
//先把数字转换成字符串
int NumberOf1Between1AndN(int n){
if(n <= 0)
return 0;
char strN[50];
sprintf(strN, "%d", n); //把整个数字转换成字符串
return NumberOf1(strN); //核心算法
}