【面试题】打印从1到最大的n位数(大数表示问题)

题目描述

在这里插入图片描述

解题思路

考虑以下两个问题:

  1. 最大的 n位数(记为 end)与位数 n之间的关系:例如,最大的 1位数是9,最大的 2位数是 99 ,最大的 3 位数是 999 ,以此类推,end=10n - 1
  2. 大数越界问题:当 n 较大时,end 会超出 int32 整型的取值范围,超出取值范围的数字无法正常存储。(但由于本题要求返回 int 类型数组,相当于默认所有数字都在 int32 整型取值范围内,因此这里不用考虑大数越界问题。感觉 LeetCode 这么设置失去了剑指offer上这题的本意。。。)

因此,通过一个 for循环生成结果列表即可,循环次数为 10n - 1。

Java

class Solution {
    public int[] printNumbers(int n) {
        int end = (int)Math.pow(10, n)-1; // Math.pow()返回值为double型
        int[] res = new int [end];
        for (int i=0; i<end; i++){
            res[i] = i+1;
        }
        return res;
    }
}

Python

class Solution(object):
    def printNumbers(self, n):
        """
        :type n: int
        :rtype: List[int]
        """
        # 先使用 range()方法生成可迭代对象,再使用 list()方法转化为列表并返回即可。
        return list(range(1, 10**n))

时间复杂度 O(10n): 生成长度为 10n的列表需使用 O(10n)时间
空间复杂度 O(1):列表作为返回结果,不计入额外空间

《剑指offer》原题解法

实际上,本题的主要考点是大数越界情况下的打印。需要解决以下三个问题:

  1. 表示大数的变量类型:无论是 short / int / long … 等基本数据类型,数字的取值范围都是有限的。因此,大数的表示应该使用字符串 String 类型。 最直观的⽅法就是字符串⾥每个字符为 ’0’ 到 ’9’ 之间的某⼀个字符, 用来表示数字中的⼀位。
  2. 进位操作:当使用 int等数字的基本数据类型时,直接通过 +1 操作即可生成下一个数字,而此方法并无法应用至 String 类型上。因此,string类型表示的数字的进位操作会比较低效,例如从 “9999” 进位到 “10000” 需要从个位、十位、百位、千位依次循环判断,操作4次进位。
  3. 避开进位操作,转为数字的全排列:实际上,可以观察发现,如果在每个数字前面补 0 凑成 n位数的话,就会发现所有的十进制 n位数其实就是 n个 ’0’ ~ ’9’ 的全排列。也就是说,我们把数字的每一位都从 0 到 9 排列一遍,就可以得到所有的十进制数,从而避免了进位操作。
  4. 递归实现全排列:先固定高位,向低位递归,直到个位上的数字确定后,生成排列结果。例如,当 n=2(数字范围 1~99)时,固定十位为 0 ~ 9中的某个数,按顺序依次向低位递归,固定个位上的数字为 0 ~ 9中的某个数,并添加该数字字符串。

在这里插入图片描述

Java

本题要求输出 int 类型数组。为 运行通过 ,可在添加数字字符串 ss 前,将其转化为 int 类型。

class Solution {
    int[] res;
    int nine = 0; // 数字中 9 的个数
    int count = 0;
    int start; // 规定字符串的左边界,以保证添加的数字字符串 num[start:] 中无高位多余的 0 。
    char[] num; // 定义表示大数的字符数组 
    char[] loop = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9'}; // 0-9 数字对应的字符
    public int[] printNumbers(int n) {
        res = new int[(int)Math.pow(10, n) - 1];
        num = new char[n]; // 申请长度为 n的字符数组表示一个 n位数
        start = n - 1; // 从个位开始
        dfs(0, n); // 递归实现全排列
        return res;
    }
    void dfs(int x, int n) {
        if (x == n) { 
            String s = String.valueOf(num).substring(start); // public String substring(int beginIndex) 返回子串
            if(!s.equals("0")) // 每次添加数字字符串前判断其是否为 "0",若为 "0" 则直接跳过。因为要生成的是从1开始的数字
                res[count++] = Integer.parseInt(s); // 每次添加字符串 s时,将其转为 int型
            if(n - start == nine) start--; // 如果当前所有位上都为9,需要执行进位操作,左边界-1,即高位上多余的“0”减少一个
            return;
        }
        for(char i : loop) {
            if(i == '9') nine++; // 在固定某一位上的数字时,如果选到排列的数字是9,则 nine+1
            num[x] = i;
            dfs(x + 1, n);
        }
        nine--; // 递归生成当前数字后,在回溯之前恢复 nine = nine -1 
    }
}

另一种写法

public class Solution {
    private List<Integer> list; 

    public int[] printNumbers(int n) {
        list = new ArrayList<>();
        dfs(n, 0, new StringBuilder()); // 从字符串的第0个位置开始递归,即从最高位开始递归
        int[] res = new int[list.size()]; // 本题要求输出 int 类型数组
        for (int i = 0; i < res.length; i++) {
            res[i] = list.get(i);
        }
        return res;
    }

    private void dfs(int n, int i, StringBuilder sb) {
        if (i == n) { // 当生成一个 n位数的字符串后,需要删除高位多余的0
            while (sb.length() != 0 && sb.charAt(0) == '0') { // 判断最高位是否为 0
                sb.deleteCharAt(0); // 循环删除索引为0 的字符,即最高位的字符
            }
            if (sb.length() != 0) {
                list.add(Integer.valueOf(sb.toString())); // 将String转换成Integer
            }
            return;
        }
        // 递归实现全排列
        for (int j = 0; j < 10; j++) {
            sb.append(j); // 每次选择 0 ~ 9 中的某一个数字
            dfs(n, i + 1, sb); // 递归对下一位上的数字进行排列
            if (sb.length() != 0) { // 当递归返回时,即上一种生产的排列结果已经检查完毕
                sb.deleteCharAt(sb.length() - 1); // 删除当前个位,等待下次循环更换另一个数
            }
        }
    }
}

按照剑指offer本题原意,采用数字字符串集拼接成的长字符串正确表示的大数。

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

参考

https://leetcode-cn.com/problems/da-yin-cong-1dao-zui-da-de-nwei-shu-lcof/solution/mian-shi-ti-17-da-yin-cong-1-dao-zui-da-de-n-wei-2/

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值