从1到n整数中1出现的次数(Java)
题目
给定一个整数 n,计算所有小于等于 n 的非负整数中数字 1 出现的个数。
示例:
输入: 13
输出: 6 
解释: 数字 1 出现在以下数字中: 1, 10, 11, 12, 13 。  public class Solution {
    public int NumberOf1Between1AndN_Solution(int n) {
        if(n <= 0){
            return  0;
        }
        if(n < 10){
            return 1;
        }
        String str = String.valueOf(n);
        int len = str.length();
        int first = str.charAt(0)-'0';
        String after = str.substring(1,len);
        int firstCount = 0;
        int SecondCount = 0;
        int third = 0;
        if(first== 1){
            firstCount = Integer.valueOf(after)+1;
        }else{
            firstCount = (int) Math.pow(10,len-1);
        }
        SecondCount = (int) ((first)*(len-1)*Math.pow(10,len-2));
        third = NumberOf1Between1AndN_Solution(Integer.valueOf(after));
        return firstCount+SecondCount+third;
    }
}  
思考一
注:(这里的 X∈[1,9] ,因为 X=0 不符合下列规律,需要单独计算)。
首先要知道以下的规律:
从 1 至 10,在它们的个位数中,任意的 X 都出现了 1 次。
从 1 至 100,在它们的十位数中,任意的 X 都出现了 10 次。
从 1 至 1000,在它们的百位数中,任意的 X 都出现了 100 次。
依此类推,从 1 至 10^ i ,在它们的左数第二位(右数第 i 位)中,任意的 X 都出现了 10^(i-1) 次。
  
以21354为例,寻找1出现的次数:
个位:从1至21350中包含了2135个10,因此1出现了2135次,21351,21352,21353,21354其中21351包含了一个1,故个位出现1的次数为:2135*10(1-1) + 1 = 2136次;
公式:( 2135+1)* 10^(1-1) = 2136;
十位:从1到21300中包含了213个100,因此1出现了213 * 10^(2-1) = 2130次,剩下的数字是21301到21354,它们的十位数字是5 > 1;因此它会包含10个1;故总数为2130 + 10 = 2140次;
公式:(213 + 1)* 10^(2-1) = 2140次;
百位:从1到21000中包含了21个1000,因此1出现了21 * 10^(3-1) = 2100次,剩下的数字是21001到21354,它们的百位数字是3 > 1;因此它会包含100个1;故总数为2100 + 100 = 2200次;
公式:(21 + 1)* 10^(3-1) = 2200次;
千位:从1到20000中包含了2个10000,因此1出现了2 * 10^(4-1) = 2000次,剩下的数字是20001到21354,它们的千位数字是1 = 1;情况稍微复杂些,354 + 1 = 355;故1的总数为2000 + 355 = 2355次;
公式:2 * 10^(4-1) + 354 + 1 = 2355次;
万位:万位是2 > 1,没有更高位;因此1出现的次数是1 * 10^(5-1) = 10000次;
公式:(0 + 1)*10^(5-1) = 10000次;
故总共为:2136+2140+2200+2355+10000=18831次;
故总结:
1、取第 i 位左边的数字(高位),乘以 10 ^(i−1) ,得到基础值 a 。
 2、取第 i 位数字,计算修正值: 
 1、如果大于 X,则结果为 a+ 10 ^(i−1) 。
 2、如果小于 X,则结果为 a 。
 3、如果等 X,则取第 i 位右边(低位)数字,设为 b ,最后结果为 a+b+1 。
 代码一
  public class Main1 {
 
	public int numberOf1Between1AndN(int n, int x){
		if(n < 0 || x < 1 || x > 9){
			return 0;
		}
		int curr, low, temp, high = n, i = 1, total = 0;
		while(high!=0){
			high = n / (int)Math.pow(10, i); //获取第i位的高位
			temp = n % (int)Math.pow(10, i); //
			curr = temp / (int)Math.pow(10, i-1); //获取第i位
			low = temp%(int)Math.pow(10, i-1); //获取第i位的低位
			if(curr == x){ //等于x
				total += high*(int)Math.pow(10, i-1)+ low + 1;
			}else if(curr < x){ //小于x
				total += high*(int) Math.pow(10, i-1);
			}else{ //大于x
				total += (high + 1) * (int)Math.pow(10, i-1);
			}
			i++;
		}
		return total;
	}
	
	public static void main(String[] args) {
		Main1 m1 = new Main1();
		int nums = m1.numberOf1Between1AndN(21354, 1);
		System.out.println(nums);
	}
}  思考2
解法一 找规律
- 第一步,最高位带来的1的个数。
 - 第二步,非最高位带来的1的个数
 - 第三步,第一段数字带来的1的个数
 
以2123为例。
将2123转成字符串s=”2123″。方便处理。
为了便于统计1的个数,将1到2123分成几段。
第一段:1~123
第二段:1000~1999
第三段:2000~2123 x124~x999。其中x124~x999就是原先124~999,将其移动2123之后的。
第一步,最高位带来的1的个数。
 这对应上面的第二段数字。
s[0]=2 > 1。此时一定含有最高位是1的数。
最高位是1的数有:1000~1999,共1000个。
这段数据形如1xxx。
数字总个数与最高位之后的数位个数有关。共3个数位,每个数位可放0-9。
总个数 = 10^( 最高位之后的数位个数 ) = 10 ^ 3=1000
但是,当最高位刚好是1时,步能这么算。例如1123.
此时,总个数 = 去掉最高位剩余的数字+1
对于1123,总个数 = 123+1=124
第二步,非最高位带来的1的个数
 这对应上面的第三段数字。 2000~2123 x124~x999
不考虑最高位,这段数字形如:Y000~Y999。
非最高位共有三个数位。每个数位都可放1。会得到多少1了?
Y100~Y199,共10^2=100个。
Y010~Y919,共10^2=100个。
Y001~Y991,共10^2=100个。
总个数 = 非最高数位个数 * 10 ^ ( 非最高数位个数 – 1)
第三步,第一段数字带来的1的个数
 1~123总共有多少个1?
这个跟1~2123总共有多少个1,没有任何区别。因此,可以递归解决。
综上
count(s,i){
    if(i>=s.length) 返回0;
    ans=0;
    //第一步
    if(s[i] == 1){
        sub=s[i+1:];
        if(sub不空) ans += sub转数字;
        ans += 1;
    }else if(s[i] > 1){
        非最高位数位个数 = s.length-i-1;
        ans += 10 ^ 非最高位数位个数;
    }
    //第二步
    非最高位数位个数 = s.length-i-1;
    ans += 非最高位数位个数 * 10 ^ (非最高位数位个数-1);
    //第三步
    ans += count(s,i+1);
    返回ans;
}
  代码2
public int numberOf1Between1AndN(int n){
        if(n<=0) {
            return 0;
        }
        String strNumber = String.valueOf(n);
        //将数字转换成字符串
        int length = strNumber.length();
        int firstDigit = strNumber.charAt(0) - '0';
        //将字符串转换成数字,Returns the char value at the specified index.
        //只有个位
        if(length == 1 && firstDigit == 0) {
            return 0;
        }
        if(length ==1 && firstDigit > 0) {
            return 1;
        }
        //除首位外,剩下的数字
        String remainedString = strNumber.substring(1);
        //从1开始到最后的位置,返回一个子字符串;substring(int beginIndex);
        // Returns a new string that is a substring of this string.
        int remainedNumber = Integer.parseInt(remainedString);
        //字符串转换成十进制数字,parseInt(String s),
        // Parses解析 the string argument as a signed decimal integer.
        //1出现在首位的次数
        int countOfFirstDigit;
        if(firstDigit == 1){
            //如果首位数字等于1,比如从10000~12345,则最高位出现1的次数为2345+1=2346次,
            countOfFirstDigit = remainedNumber +1;
            //(2)是把除高位外的字符串转化为整数。10000~12345中最高位出现1个次
            // 数为除去最高数字滞后于剩下的数字再加1.
        } else{
            //(1)1345~21345中,万位上出现1的数字在10000~19999中,有10^4个。
            // 如果n的长度为length,则共有10^(length-1)次。
            countOfFirstDigit = (int)Math.pow(10,length-1);
        }
        //(length-1):任选一位为1;10^(length0-2):其余位从0-9中选;
        //firstDigit:首位可选的数字
        //除了第一位的数之外,其他位上有1的次数:再把1346~21345分成2段,1346~11346,11236~21346,
        //每一段中除去其中的一个数为1之外,其他的每位均可以取0~9,所以有2*10^3次。即共有first*10^(length-2)次
        int countOfOtherDigit = firstDigit *(length -1)*(int)Math.pow(10, length-2);
        // 递归求在剩下数字中1出现的次数
        int remainedCount = numberOf1Between1AndN(remainedNumber);
        int result = countOfFirstDigit + countOfOtherDigit + remainedCount;
        return result;
    }
  

京公网安备 11010502036488号