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

题目

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

示例 1:

输入: n = 1
输出: [1,2,3,4,5,6,7,8,9]

说明:

  • 用返回一个整数列表来代替打印
  • n 为正整数

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/da-yin-cong-1dao-zui-da-de-nwei-shu-lcof

解题思路

不考虑大数版本情况下很简单,其实就是依次打印1 ~ 10n - 1

主要考虑的还是大数越界情况下的打印

  • 使用字符串表示数值
  • 每一位都是0 ~ 9字符的全排列
  • 怎么消除全排列前面无意义的0
  • 从1开始打印,0不要

以n = 3为例
主要难点就是消除全排列前面无意义的0,
9,99,999…的时候,都需要进位,所以主要还是看字符中9的个数

假设左边界为start,初始化时start = n - 1,因为数组长度为n时,最后一位下标为n - 1
假设字符数组中’9’的个数为nine,初始化时为0,每一次循环碰到’9’就进行加一,循环结束减一
当字符数组为9,99,999时nine为1,2,3,此时start分别为2,1,0,则有n - start = nine

代码

不考虑大数

class Solution {
    public int[] printNumbers(int n) {
        // 10的次方减一
        int num = (int)Math.pow(10, n) - 1;
        // 1 ~ num就是num个空间
        int[] array = new int[num];
        for (int i = 0; i < num; i++) {
            array[i] = i + 1;
        }

        return array;
    }
}
  • 时间复杂度:O(10n),生成长度为 10n的列表需使用 O(10n)时间
  • 空间复杂度:O(1),建立列表使用O(1)大小的额外空间,列表作为返回结果,不计入额外空间

考虑大数,返回字符串

class Solution {
    // 全排列使用到的字符数组
    private char[] cArr = {'0','1','2','3','4','5','6','7','8','9'};
    // 用来存储元素
    private LinkedList<String> tempList;
    // 字符数组,用来存储全排列的字符串
    private char[] numArr;
    // 左边界值,用来消除前面的0
    private int start;
    // 用来记录9的个数,9,99,999的时候需要start--
    private int nine;

    public String printNumbers(int n) {
        // 考虑大数版本
        // 初始化
        tempList = new LinkedList<>();
        numArr = new char[n];
        // 初始化左边界为n - 1,即从字符数组的最后一个下标开始
        start = n - 1;
        nine = 0;
        dfs(0, n);
        // 删除第一个元素,因为从1开始,删除0
        tempList.removeFirst();
        return String.join(",", tempList);
    }

    private void dfs(int index, int n) {
        // 递归结束条件,字符数组下标越界
        if (index == n) {
            // 字符数组转字符串
            String s = new String(numArr).substring(start);
            tempList.add(s);
            // 当所有位都为9的时候,左边界值减一
            if (n - start == nine) {
                start--;
            }
            return;
        }

        // 迭代字符数组
        for (char c : cArr) {
            // 当前字符为9时,计数器加一
            if (c == '9') {
                nine++;
            }
            // 赋值当前下标
            numArr[index] = c;
            // 递归调用
            dfs(index + 1, n);
        }
        // 撤销计数器
        nine--;
    }

}
  • 时间复杂度:O(10n),生成长度为 10n的列表需使用 O(10n)时间
  • 空间复杂度:O(10n),使用列表的长度10n - 1

考虑大数,返回int数组

class Solution {

    // 全排列使用到的字符数组
    private char[] cArr = {'0','1','2','3','4','5','6','7','8','9'};
    // 字符数组,用来存储全排列的字符串
    private char[] numArr;
    // 返回的数组
    private int[] array;
    // 左边界值,用来消除前面的0
    private int start;
    // 用来记录9的个数,9,99,999的时候需要start--
    private int nine;
    // 数组下标
    private int count;

    public int[] printNumbers(int n) {
        // 考虑大数版本
        // 初始化
        int num = (int)Math.pow(10, n) - 1;
        array = new int[num];
        numArr = new char[n];
        // 初始化左边界为n - 1,即从字符数组的最后一个下标开始
        start = n - 1;
        nine = 0;
        count = 0;
        dfs(0, n);
        return array;
    }

    private void dfs(int index, int n) {
        // 递归结束条件,字符数组下标越界
        if (index == n) {
            // 字符数组转字符串
            String s = new String(numArr).substring(start);
            if (!s.equals("0")) {
                array[count++] = Integer.parseInt(s);
            }
            // 当所有位都为9的时候,左边界值减一
            if (n - start == nine) {
                start--;
            }
            return;
        }

        // 迭代字符数组
        for (char c : cArr) {
            // 当前字符为9时,计数器加一
            if (c == '9') {
                nine++;
            }
            // 赋值当前下标
            numArr[index] = c;
            // 递归调用
            dfs(index + 1, n);
        }
        // 撤销计数器
        nine--;
    }

}
  • 时间复杂度:O(10n),生成长度为 10n的列表需使用 O(10n)时间
  • 空间复杂度:O(1),建立列表使用O(1)大小的额外空间,列表作为返回结果,不计入额外空间
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值