【剑指offer】43. 1~n 整数中 1 出现的次数

题目描述

在这里插入图片描述

在这里插入图片描述


// 力扣
// 输入一个整数 n ,求1~n这n个整数的十进制表示中1出现的次数。

// 例如,输入12,1~12这些整数中包含1 的数字有1、10、11和12,1一
// 共出现了5次。

 
// 牛客

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

题解

// 题目比较难,需要比较数学的处理。我们将个位,十位,百位等等这些位分开讨论。
// 比如n=126,1出现的次数 = (个位的1出现次数)+(十位的1出现次数)+(百位的1出现次数)


// 力扣
// 这里是无注释的干净算法代码,注释版在下面
// 执行用时:0 ms, 在所有 Java 提交中击败了100.00%的用户
// 内存消耗:35 MB, 在所有 Java 提交中击败了86.22%的用户
class Solution {
    public int countDigitOne(int n) {
		int count = 0;
		int weight = 0;
		int low = 0;
		int high = n;
		for (int base = 1; base <= n; base *= 10) { 
			weight = high % 10;  
			high = high / 10;
			low = n % base;
			if (weight > 1)
				count += (high * base + base);
			else if (weight == 1)
				count += (high * base + low + 1);
			else 
				count += (high * base);
		}
		return count;
	}
}


// 力扣
// 下面是代码注释
// 执行用时:0 ms, 在所有 Java 提交中击败了100.00%的用户
// 内存消耗:35 MB, 在所有 Java 提交中击败了86.22%的用户
class Solution {
    public int countDigitOne(int n) {
		int count = 0;   // 保存答案,记录1出现的次数
		int weight = 0;  // 位指针,从个位开始到十位 百位,直至最高位遍历
		int low = 0;     // 位指针外的低位,初始化为0
		int high = n;    // 位指针外的高位,初始化为n
		// for循环遍历n的位数,weight从最低位个位到n的最高位遍历,
		// 遍历位的基数记为base。
		// 比如n=126,则位指针weight从低到高将遍历6(个位)2(十位)1(百位)
		// 则base分别为1(个位),10(十位),100(百位)。
		for (int base = 1; base <= n; base *= 10) { 
			// 计算位指针weight,高位high÷10取余即可得到,
			// 如n=126, 则weight=6(个位)
			weight = high % 10;  
			// 高位high随着位指针weight的移动而左移,由于high初始化为n
			// 之后算high直接high÷10取整即可。
			// 假如n=126,位指针weight=6,则high = 126 / 10 = 12(取整)
			// 正好是6左边的数,可见位指针weight和高位high的计算是除法的
			// 两种取法。高位high每次÷10时,取整就可以左移得到下一次遍历的高位high,
			// 取余就可以得到下一次遍历的位指针weight。我们利用这个特点
			// 将高位high初始化为n,就可以得到第一个位指针weight。
			high = high / 10;
			// 低位的计算比较巧妙,在循环中,位指针weight从个位开始不断左移
			// 的同时,我们记录了位指针weight所在位的基数base。
			// 假如n=126,位指针weight指向了2,那么所在位的基数base为10(十位)
			// 低位low通过n÷base取余可以得到。换句话说,由于得知位指针weight的
			// 位置和基数,通过除法取余可以得到位指针weight右边的所有内容。
			low = n % base;
			// 计数方法:【"1"出现的次数=(个位的"1"出现次数)+(十位的"1"出现次数)+...】
			// 根据这个原则,指针weight移动到哪位,我们就计算哪位的"1"的出现次数。
			// 当指针遍历的位数字大于1时,该位出现"1"的次数,肯定包含高位数字本身
			// 并且还包含当前位的基数。为什么呢?
			// 假如n=126,指针weight指向6,因为是个位,首先基数base是1。
			// weight>1,个位要到达6,就必须出现过一次"1",所以要+base
			// 如果指针weight指向2,因为是十位,首先base是10,
			// weight>1,十位要达到2,就必须出现过十次"1",所以还是要+base。
			// weight为其他位时也同理。
			
			// 那么加上高位high * base意思也是一样的,n=126,
			// 指针weight指向6,则指针位肯定出现过base次的"1",高位high为12,
			// 能到达n = 12*base + 6*base这么大的数,weight遍历位已经出现过
			// 12 * base轮的"1"了。所以还要加 high * base。
			if (weight > 1)
				count += (high * base + base);
			// 若指针位weight==1,加high * base是不变的,但是由于weight不大于1
			// ,到达当前指针weight这个数时出现的"1"可能不满base次,所以不能直接+base
			// ,因为weight是1,每个和weight搭配的low都会使遍历位出现1次"1",
			// 所以出现了low次"1",再加上low=0还会出现一次"1","1"总共出现了low+1次。
			// 比如weight=1,low=4,那么遍历位的"1"肯定出现了4 + 1次,分别是
			// 10,11,12,13,14。
			else if (weight == 1)
				count += (high * base + low + 1);
			// 如果指针位weight为0,那么当前遍历位和低位都不会对当前遍历位
			// 出现"1"有帮助。当前位出现"1"只可能来源于高位high,直接加 high * base
			else 
				count += (high * base);
		}
		return count;  // 循环结束,返回count
	}
}


// 牛客
// 运行时间:11ms
// 占用内存:9652k
public class Solution {
    public int NumberOf1Between1AndN_Solution(int n) {
		int count = 0;
        int weight = 0;
        int high = n;
        int low = 0;
        for (int base = 1; base <= n; base *= 10) {
            weight = high % 10;
            high = high / 10;
            low = n % base;
            if (weight > 1)
                count += (high * base) + base;
            else if (weight == 1)
                count += (high * base) + low + 1;
            else 
                count += (high * base);
        }
        return count;
    }
}

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/fisherish/article/details/113243394
今日推荐