剑指offer:把数组排成最小的数和整数中 1 出现的个数

本文探讨了将数组中的数字拼接成最小数的排序算法,并深入分析了计算任意非负整数区间中数字1出现次数的有效方法。通过具体实例,展示了如何利用排序和数学公式解决这两类问题。

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

把数组排成最小的数
输入一个正整数数组,把数组里所有数字拼接起来排成一个数,打印能拼接出的所有数字中最小的一个。例如输入数组{3,32,321},则打印出这三个数字能排成的最小数字为321323。

分析
这题肯定要使用排序,但是要怎么使用排序我想了好久都没有想明白,后来看了答案,才发现原来还可以这样排序。积累经验,代码倒是很简单,注意一下 sort 的用法就行了。

static bool cmp(int a, int b) {
	string a1 = to_string(a);
	string b1 = to_string(b);
	return a1 + b1 < b1 + a1;
}

string PrintMinNumber(vector<int> numbers) {
	sort(numbers.begin(), numbers.end(), cmp);
	string ret = "";
	for (vector<int>::iterator iter = numbers.begin(); iter != numbers.end(); ++iter) {
		ret += to_string(*iter);
	}
	return ret;
}

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

分析
最简单的就是暴力解法。我知道这题肯定有简单的做法,但我想了好久还是没做出来,只好看别人的答案了。
先把暴力解法的代码贴上来吧。

int count_1(string n) {
	int cnt = 0;
	for (int i = 0; i < n.size(); ++i) {
		if (n[i] == '1') {
			++cnt;
		}
	}
	return cnt;
}
// 学到了 to_string 这个函数
int NumberOf1Between1AndN_Solution(int n)
{
	int cnt = 0;
	for (int i = 1; i <= n; ++i) {
		string str_i = to_string(i);
		cnt += count_1(str_i);
	}
	return cnt;
}

另一种更快的方法
先给出 1 的个数的计算公式:对于第 i 位(第一位:个位,第二位:十位)上的数字 X,将其与 数字 1 进行比较:

  • X>1: (高位数字+1)*10^(i-1)
  • X<1: (高位数字)*10^(i-1)
  • x=1: (高位数字)*10^(i-1)+(低位数字+1)
    (高位数字与低位数字:假设一个数字12345 ,对于七张的 3,高位数字为 12, 低位数字为 45)

然后给出一个共识:
10 里,1 作为个位出现了 1 次
100 里,1 作为 10 位出现了 10次
1000 里,1 作为百位出现了 100 次
以此类推:从 1到 (10^i)中,1 在对应位上出现 10^(i-1) 次

再用一个数字 3014 来举例说明:
分别看个、十、百、千上各有多少个 1
个位:有 301 个 10(每个 10 里有一个 1),再加上 3011里的个位 1,所以 个位中 1 的个数为 301+1=302。
对应公式:(301+1)*10^(1-1)=302
十位:有 30 个 100,十位数字为 1,个位为 4,有 5 个 1 (0~4),所以十位中有 30*10+5=305 个 1。
对应公式:30*10^(2-1)+4+1=305
百位:有 3 个 1000,则百位中 1 的个数为:3*10^(3-1)=300 个。
对应公式:3*10^(3-1)=300
千位:0 个 10000,没有更高位,1 作为 千位出现了 1000 次
对应公式:(0+1)*10^(4-1)=1000
最后把它们相加。

代码就偷个懒·不写了·。。。

参考文献
[1] 从1到n整数中1出现的次数
[2] 从1到n整数中1出现的次数

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值