题目
输入数字n, 按顺序打印出从1到最大的n位十进制数,比如数3,则打印出1,2,3….一直到3位数最大即999
解析
预备知识
因为题目没有说数据位数范围,所以这里应该为大数问题(即数的表示方式已超出编程语言自带的数据结构表示范围),需要自定义数据结构来表示大数。一般有2种方式表示大数:
1. 用字符数组,char[]来表示大数,其中数组每一个元素对应数中的每一位
2. 用字符串来表示大数,字符串中的每一位表示数的每一位
思路一
由于题目给定了数的位数,所以此处用字符数组比较方便。之后我们就对每个数进行自增操作即可,每自增一次就打印一次即可。直到数的位数大于n位。那么如何更快判断当前的数已经超出n位了呢?
1. 可以根据给定n,构造出n位最大的字符串,然后每次循环都与该字符串比较是否相等即可。比如输入5,那么每次自增完与99999判断是否相等来决定是否结束打印即可。字符串比较相等的复杂度为O(n)
2. 我们可以发现超出n位的最小的数最高位为1,这样我们可以判断char[0] 是否等于 ‘1’来判断是否超出了n位最大数,复杂度为O(1)
public static void printToMaxOfNDigits1(int n) {
if(n <= 0) {
return;
}
char[] nums = new char[n + 1];
Arrays.fill(nums, '0');
while(!increment(nums)) {
printNum(nums);
}
}
/**
* 大数自增操作!
* 并判断是否已经超出了n位了。
* @param nums
* @return
*/
public static boolean increment(char[] nums) {
int carry = 0;
for(int i = nums.length - 1; i >= 0; i--) {
int temp = nums[i] - '0' + carry;
//因为是加1,所以肯定是在最后一位上加1了
if(i == nums.length - 1) {
temp++;
}
carry = temp / 10;
temp %= 10;
nums[i] = (char)(temp + '0');
}
return nums[0] == '1';
}
public static void printNum(char[] nums) {
int index = 0;
for(; index < nums.length; index++) {
if(nums[index] != '0'){
break;
}
}
for(; index < nums.length; index++) {
System.out.print(nums[index]);
}
System.out.println();
}
思路二
我们发现数的每一位都是0到9,所以可以利用全排列的思想,凭借递归的优雅实现达到目的。比如输入3,我们数的打印顺序为:
001
002
…
010
011
012
…
100
101
…
999
我们从高位开始,对于每一位都递归的用0-9填充,最后递归结束条件为已考察到所有的位,打印并且回溯到最后一位,重新对该位赋予下一个值。
public static void printToMaxOfNDigits2(int n) {
if(n <= 0) {
return;
}
char[] nums = new char[n];
recursiveProductNum(0, n, nums);
}
public static void recursiveProductNum(int index, int length, char[] nums) {
if(index == length) {
printNum(nums);
return;
}
for(char i = '0'; i <= '9'; i++){
nums[index] = i;
recursiveProductNum(index + 1, length, nums);
}
}
public static void printNum(char[] nums) {
int index = 0;
for(; index < nums.length; index++) {
if(nums[index] != '0'){
break;
}
}
for(; index < nums.length; index++) {
System.out.print(nums[index]);
}
System.out.println();
}