求一个整数区间(从1到n)内1出现的次数

题目描述

求出1-13的整数中1出现的次数,并算出100-1300的整数中1出现的次数?为此他特别数了一下1~13中包含1的数字有1、10、11、12、13因此共出现6次,但是对于后面问题他就没辙了。ACMer希望你们帮帮他,并把问题更加普遍化,可以很快的求出任意非负整数区间中1出现的次数(从1 到 n 中1出现的次数)。

暴力解法

比较简单的解法是从1到n顺次遍历,每遍历到一个数,依次求其每一位是否为1

        //暴力法,时间复杂度是O(nlogn)
        // 遍历1~n,不断迭代检查各位是不是1
        int count = 0;
        for(int i = 1;i <= n;i++){
            int a = i;
            while(a != 0){
                if(a % 10 == 1){
                    count++;
                }
                a = a / 10;
            }
        }
        return count;

我们也可以从数学的角度进行优化,寻找规律。

从简单的开始思考

如果求从1到99中1的个数,该怎么求?

可以很自然的将1到99划分为1到9的个位数以及10到99的双位数来求解。

1到9只有一个1,而10到99其实可以通过排列组合的思想来求解。
10到99这个范围包含了所有的两位数,在这个区间内,十位和个位都可以去0-9的任意10个数。
因此,我们可以分别让10到99这个区间的任意一个数的十位置1,个位可以在0-9内随意变动,同理,个位也是如此,因此我们可以求出,在10-99这个区间内,总共有10*2=20个1.


引申至任意的n

可以随便举个例子,比如n=24563。作为一个5位数,我们可以按照上文的思路将其分为一个五位数的区间和一个四位数,该四位数可以继续往下递归。

但是在每一层该如何求解是个问题,按照上文的思路,如果可以通过排列组合的思想来求解的话,我们就可以非常灵活的根据位数求解出1的个数,但是排列组合思想的前提是每一位都能从0-9随意取值。

因此鉴于上文的思路,我们可以将1-24563拆解为1-4563和4564-24563。

为什么可以这样拆解呢?我们可以先尝试按照这个思路算一算。

4564-24563有四位数也有五位数,我们可以首先将五位数可能出现的1算出来。

从10000-19999的五位数都是1,所以五位数可能出现的1的次数为10^4=10000个。

接下来算其余的四位数,由上文可知,可以通过排列组合的方式迅速求解。但是上文用排列组合是从10-99,简单来说就是每一位数都能任意从0-9取值。我们这里可以吗?

从这里就可以看出我们这样划分的思路了,从4564-24563,刚好20000个数,可以进一步划分为4564-14563和14564-24563,每一个区间从个位都千位都能任意取0-9。

由前文排列组合的规律可以轻易得到从个位到千位的所有1的个数是2410^3=8000。由此可得从4564-24563所有1的个数为18000。


附代码(java)

```
public class Solution {
private int num = 0;
public int NumberOf1Between1AndN_Solution(int n) {
// 寻找数学规律,递归求解
int a = n;
int count = 0;
while(a != 0){
a = a / 10;
count++;
}
bit(n, count);
return num;
}
private void bit(int n, int count){
if(n == 0){
return;
}
if(n < 10){
num++;
return;
}
if(n / pow(10, count - 1) > 1){
num += pow(10, count - 1);
}else{
num += (n % pow(10, count - 1) + 1);
}
num += (n / pow(10, count - 1)) * (count - 1) * pow(10, count - 2);
bit(n % pow(10, count - 1), count - 1);
}
// java的幂指数运算返回的是double型,我们可以自己定义一个返回整型的求幂方法
private int pow(int i, int n){
if(n == 0){
return 1;
}
int a = i;
while(n > 1){
i *= a;
n--;
}
return i;
}
}

原文地址:https://www.cnblogs.com/Water2Wine/p/12416617.html

时间: 2024-08-29 20:21:57

求一个整数区间(从1到n)内1出现的次数的相关文章

求一个整数的位数

C++ 吧里看到有人求助, 说是想写点代码求一个整数的位数, 以下是我的解决方法: int32_t RecursionIter (const int64_t& num, const int32_t& result) { if (num < 0) { return RecursionIter (-num, result); } else if (num / 10 == 0) { return result; } else { return RecursionIter (num / 10

求一个整数的最大质因数(回答一个群友的问题)

[QQ群: 189191838,对算法和C++感兴趣可以进来] 群里最近每天都聊得很嗨,虽然不一定是技术,更多的是生活,还是非常开心.因为工作.程序只是我们生命中的一小部分,人的幸福与否,更多的由人的业余生活和他的精神世界所决定.和一群陌生的人,扯扯淡,毫无压力的聊聊人生觉得也挺好. 好的,直接奔入主题.情况是这样的,群里有位同学问了一道算法,求一个整数的最大质因数.然后他给出了代码(如下所示),但是测试没有通过. 1 int largest_prime_factor(int n) 2 { 3

求一个整数的二进制中1的个数

题目:输入一个整数,求该整数的二进制表达中有多少个1.例如输入10,由于其二进制表示为1010,有两个1,因此输出2. 假设该整数为i.首先i和1做与运算,判断i的最低位是不是为1.接着把1左移一位得到2,再和i做与运算,就能判断i的次高位是不是1……这样反复左移,每次都能判断i的其中一位是不是1.基于此,我们得到如下代码 int NumberOf1_Solution(int i) { int count = 0; unsigned int flag = 1; while(flag) { if(

高效求一个整数中1的位数

求一个整数中0或1的位数,有很多方法可以使用除法,求余等方法,也可以使用位运算,相比前者效率更高. #include <stdio.h> #include <stdlib.h> //求一个整数 1的位数 int count0(int x) { int num=0; while(x) { num+=x%2; x/=2; } return num; } int count1(int x) { int num=0; while(x) { num+=(x&0x01); x>&

求一个整数中二进制1的个数

题目:求一个整数二进制表示1的个数 第一版: 思路:如果一个整数与1做与运算,结果为1,那么该整数最右边一位是1,否则是0: int NumberOf1(int n) { int count = 0; while (n) { if (n&1)//如果一个整数与1做与运算的结果是1,表示该整数最右边是1,否则是0: { count++; } n = n>>1; } return count; } 缺点:因为代码当中有右移,当是负数的时候,要考虑符号位:如果一个正数,右移之后在最左边补n个

c/c++ 求一个整数转换为二进制数时中‘1’的个数

求一个正整数转换为二进制数时中'1'的个数 分析:这道题目就是很简单的位运算,我们可以把这个整数和1进行&操作(就是二进制数中的最低位与1进行&),然后将这个整数进行右移处理,将下个位置的数字移到最低位:反复如此操作直到该数变为0. 1 int NumOf_one(int k) 2 { 3 int count_1 = 0; 4 while(k) 5 { 6 if(k & 1) 7 count_1++; 8 k >>= 1; 9 } 10 return count_1;

求一个整数数组的最大连续子数组的和

输入一个整数数组,数组里有正数也有负数. 数组中连续的一个或多个整数组成一个子数组,每个子数组都有一个和. 求所有子数组的和的最大值. int max(int a,int b) { if(a>b) { return a; } else { return b; } } int maxsum(int a[], int n) { int i; int maxsofar = 0; int maxendinghere = 0; for (i = 0; i < n; i++) { maxendingher

C 语言实例 - 求一个整数的所有因数。

假如a*b=c(a.b.c都是整数),那么我们称a和b就是c的因数. #include <stdio.h>int main(){    int number, i;    printf("输入一个整数: ");    scanf("%d",&number);    printf("%d 的因数有: ", number);    for(i=1; i <= number; ++i) {    if (number%i ==

求一个整数的平方和立方

#include<iostream> using namespace std; int main() { int i,n,m; cout<<"please enter i:"; cin>>i;  if(n=i*i) cout<<n<<endl; if(m=i*i*i) cout<<m; return 0; } //任意输入一个值求他的平方和立方 #include<iostream> using name