剑指Offer系列(java版,详细解析)17.打印从1到最大的n位数

题目描述

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

难度简单93

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

示例 1:

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

说明:

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

测试用例

  • 功能测试(输入1、2、3…)
  • 特殊输入测试(输入-1、0)

题目考点

  • 考察应聘者解决大数问题的能力。面试官出这道题的时候,他期望应聘者能意识到这是一个大数问题,同时还期待应聘者能定义合适的数据结构表示方式来解决大数问题。

解题思路

在字符串上模拟数字加法的解法

我们用字符串来表示 n ,首先把字符串的每一个数字都转化为‘0’,然后每次为字符串表示的数字加1,再打印出来。因此我们需要做两件事:一是在字符串表示的数字上模拟加法;而是把字符串表示的数字打印出来。

把问题转成数字排列的解法

如果我们在数字前面补零,就会发现 n 位十进制数其实就是 n 个从 0 到 9 的全排列。也就是说,我们把数字的每一个为都从 0 到 9 排列一遍,就得到了所有的十进制数。只是在打印的时候,排在前面的0不打印出来罢了。

普通解题

/**
 * 打印从1到最大的n位数
 *
 */
public class Solution {
    public void print1ToMaxOfNDigits(int n) {
        int number = (int) Math.pow(10, n);
        for (int i = 1; i < number; i++) {
            System.out.println(i);
        }
    }
}

考虑欠缺点:

  • 没有考虑n是一个大数,没有意识到这是一个大数问题。
  • 没有考虑非法输入,如负数。

参考解题

在字符串上模拟数字加法的解法

/**
 * 打印从1到最大的n位数
 *
 * @Author rex
 * 2018/7/22
 */
public class Solution1 {
    
    
    /**
     * 在字符串上模拟数字加法的解法
     *
     * @param n
     */
    public void print1ToMaxOfNDigits(int n) {
    
    
        // 防止非法输入
        if (n <= 0) {
    
    
            return;
        }
        char[] number = new char[n];
        initCharArray(number);
        while (!increment(number)) {
    
    
            printNumber(number);
        }
    }


    /**
     * 字符串表示的数字上模拟加法
     * @param number
     * @return
     */
    private boolean increment(char[] number) {
    
    
        // 是否超出999....
        boolean isOverflow = false;
        // 进位数
        int takeOver = 0;
        for (int i = number.length-1; i >= 0; i--) {
    
    
            int sum = number[i] - '0' + takeOver;
            if (i == number.length-1) {
    
    
                sum++;
            }
            if (sum >= 10) {
    
    
                if (i == 0) {
    
    
                    isOverflow = true;
                } else {
    
    
                    sum -= 10;
                    takeOver = 1;
                    number[i] = (char) ('0' + sum);
                }
            } else {
    
    
                number[i] = (char) ('0' + sum);
                break;
            }
        }
        return isOverflow;
    }

    /**
     * 根据字符串打印出数字
     * 这个我一会也想出来
     * @param number
     */
    private void printNumber(char[] number) {
    
    
        // 默认字符串不以0开始
        boolean isBegining0 = true;

        for (int i = 0; i < number.length; i++) {
    
    
            if (isBegining0 && number[i] != '0') {
    
    
                isBegining0 = false;
            }
            if (!isBegining0) {
    
    
                System.out.print(number[i]);
            }
        }
        System.out.println();
    }

    /**
     * 初始化字符数组
     *
     * 使其每个字符初始为'0'
     * @param chars
     */
    public void initCharArray(char[] chars) {
    
    
        for (int i = 0; i < chars.length; i++) {
    
    
            chars[i] = '0';
        }
    }

}

把问题转成数字排列的解法

1.因为要考虑到大数,而int/long的取值范围都是有限的,所以应该用String字符串类型来表示大数,我们先把每个数当成一个size为n的字符数组,得到字符数组后,再字符数组->字符串->整数,转换为整数的过程可以自动把高位的0去掉 2.从第一位开始对每一位数字递归生成全排列,例如:n=2时,固定第一位为0,dfs(1,2),得到00,01,02,03,04,…09, 这些前面有0的字符数组,我们先用String.valueOf方法转换为字符串,再用Integer.valueOf方法转换为整数,然后添加到结果数组中(注意:要先跳过0)

class Solution {
    
    
    //先定义几个需要用到的全局变量
    //初始化结果数组res
    int[] res; //Math.pow返回的是double
    char[] nums;
    char[] loop = {
    
    '0','1','2','3','4','5','6','7','8','9'}; 
    int count=0;//数组计数索引
    int x = 0; //x表示递归到第几层
    public int[] printNumbers(int n) {
    
    
        nums = new char[n];
        res = new int[(int)Math.pow(10,n)-1]; //Math.pow返回的是double
        dfs(x,n);
        return res;
    }

    public void dfs(int x,int n){
    
      //x表示递归到第几层
        //递归终止条件,x==n,此时要要结果数组res中添加数字
        if(x==n){
    
    
            String str = String.valueOf(nums);//把字符数组转换为字符串
            int temp =  Integer.valueOf(str); //把字符串转换为整数
            if(temp!=0){
    
      //跳过0          
                res[count] = temp ;//把字符串转换为整数后添加到结果数组中
                count++; //计数索引加1
                return; 
            }
            return;
        }

        for(char ch:loop){
    
     //对第x层的数字进行固定,然后继续向下一层递归
            nums[x] = ch;
            dfs(x+1,n);
        }
    }
}

补充

如果面试题是关于 n 位的整数并且没有限定 n 的取值范围,或者输入任意大小的整数,那么这道题很有可能是需要考虑大数问题的。字符串是第一种简单、有效地表示大数的方法。

猜你喜欢

转载自blog.csdn.net/weixin_43314519/article/details/114704475