剑指Offer 17、打印从1到最大的n位数

119 阅读2分钟

一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第13天,点击查看活动详情

题目: 输入数字 n,按顺序打印出从 1 到最大的 n 位十进制数。比如输入 3,则打印出 1、2、3 一直到最大的 3 位数 999。

解题思路

上篇文章已经说过最简单的方式,找到每位数一共需要打印多少个数,循环即可。但int类型的数存在越界的问题,例如此时的n如果超过31,则会出现越界,此时就无法得到正确的结果,解决方法是将得到的数字转为long,但long类型的数字也是存在越界情况的,因此无法通过这种常规的思路进行解决。

如果n=1,那么此时打印1-9,如果n=2,那么此时打印1-99。因此可得,当输入为n时,则打印的为1-10n110^n-1。那么转换一下,当n为2的时候,就是两个['0', '1', '2', '3', '4', '5', '6', '7', '8', '9']的全排列问题,因此根据这个思路,我们可以使用深度优先搜索,每个元素都遍历一次上面的数组,当已记录元素的长度和N的长度相等的时候则停止,此时将结果加入集合,代码如下:

public String printNumbers(int n){
    char[] c = new char[]{'0', '1', '2', '3', '4', '5', '6', '7', '8', '9'};
    char[] num = new char[10];
    StringBuilder res = new StringBuilder();
    dfs(n, 0, c, res, num);
    res.deleteCharAt(res.length()-1);
    return res.toString();
}

public void dfs(int n, int cur, char[] c, StringBuilder sb, char[] res){
    if(cur == n) {
        sb.append(String.valueOf(res) + ",");
        return;
    }
    for (char c1 : c) {
        res[cur] = c1;
        dfs(n, cur+1, c, sb, res);
    }
}

但上述方法输出的结果为00, 01, 02, ... , 99,即结果是从00开始,并且小于10的元素前都会存在0,这是不符合要求的。

此时我们可以记录9的个数来去掉0,代码如下:

class Solution {
    int[] res;
    int nine = 0, count = 0, start, n;
    char[] num, loop = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9'};
    public int[] printNumbers(int n) {
        this.n = n;
        res = new int[(int)Math.pow(10, n) - 1];
        num = new char[n];
        start = n - 1;
        dfs(0);
        return res;
    }
    void dfs(int x) {
        if(x == n) {
            String s = String.valueOf(num).substring(start);
            if(!s.equals("0")) res[count++] = Integer.parseInt(s);
            if(n - start == nine) start--;
            return;
        }
        for(char i : loop) {
            if(i == '9') nine++;
            num[x] = i;
            dfs(x + 1);
        }
        nine--;
    }
}