剑指offer第二版面试题17:打印1到最大的n位数(java)

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

跳进面试官的陷阱:
这个题目看起来很简单。我们看到这个问题后,最容易想到的办法是求出最大的n位数,然后用一个循环从1开始逐个打印。于是我们很容易写出下面的代码。

public static void print1ToMaxOfNDigits(int n) {
        int number = (int) Math.pow(10, n);
        for (int i = 1; i < number; ++i)
            System.out.println(i);
    }

初看之下没有问题,但如果仔细分析这个问题,我们就能注意到面试官没有规定n的范围。当输入n很大的时候,我们求最大的n位数是不是用整型(int)或者长整型(long)都会溢出?也就是说我们需要考虑大数问题。这是面试官在这道题里设置的一个大陷阱。

在数组上模拟数字加法的解法,绕过陷阱才能拿到offer:
经过前面的分析,我们很自然的想到解决这个问题需要一个大数。最常用的也是最容易的用字符串或者数组表达大数。接下来我们用数组来解决大数问题。
代码如下:

/**
 * 打印从1到最大的n位数:使用数组表示大数
 */
public class PrintOneToMaxNthDigits1 {

    // 使用数组实现对数进行+1操作
    public static boolean increment(int[] number) {
        // 最高位产生进位标志
        boolean isOverFlow = false;

        // 进位位
        int carry = 0;

        for (int i = number.length - 1; i >= 0; i--) {
            int sum = number[i] + carry;
            if (i == number.length - 1) {
                sum++;
            }
            if(sum >= 10){
                if(i == 0)
                    isOverFlow = true;
                else{
                    sum = sum - 10;
                    carry = 1;
                    number[i] = sum;
                }
            }else{
                number[i]++;
                break;
            }
        }
        return isOverFlow;
    }

    // 打印数组中表示的数,如果数组中表示的数字位数小于n,则不打印前面的0
    public static void print(int[] number) {
        // 标记:判断是否可以开始打印
        boolean isBeginning = false;
        for (int i = 0; i < number.length; i++) {
            if (!isBeginning && number[i] != 0) {
                isBeginning = true;
            }
            if (isBeginning)
                System.out.print(number[i]);
        }
        System.out.println();
    }

    public static void main(String[] args) {
        //使用数组来模拟大数
        int[] number = new int[3];
        while(!increment(number)){
            print(number);
        }
    }
}

Increment要注意终止条件
打印函数:要注意处理前面的‘0’字符

把问题转化成数值排列的解法,递归让代码更简洁:
用数字排列的方法表示:如果我们在数字前面补0的话,就会发现n位所有十进制数其实就是n个从0到9的全排列。也就是说,我们把数字的每一位都从0到9排列一遍,就得到了所有的十进制数。当然打印的时候,我们应该将前面的0补位去掉。
代码如下:

/**
 * 打印从1到n位的最大数:使用递归实现全排列
 */
public class PrintOneToMaxNthDigits2 {

    //打印数
    public void printNumber(StringBuffer sb){
        boolean flag = false;

        for(int i = 0; i < sb.length(); i++){
            if(!flag && sb.charAt(i) != '0'){
                flag = true;
            }
            if(flag){
                System.out.print(sb.charAt(i));
            }
        }
        if(flag)
            System.out.println();
    }

    //打印从1到n位的最大数
    public void Print1ToMaxOfNDigits(int n){
        if(n <= 0)
            return ;

        //初始化数字(用StringBuffer表示)
        StringBuffer sb = new StringBuffer(n);
        for(int i = 0; i < n; i++){
            sb.append('0');
        }

        print1ToMaxOfNDigits_Recursely(sb, n, 0);
    }

    public void print1ToMaxOfNDigits_Recursely(StringBuffer sb, int n, int index){
        if(index == n){
            printNumber(sb);
            return ;
        }

        for(int i = 0; i < 10; i++){
            sb.setCharAt(index, (char)(i+'0'));
            print1ToMaxOfNDigits_Recursely(sb, n, index+1);
        }
    }

    public static void main(String[] args) {
        PrintOneToMaxNthDigits2 test = new PrintOneToMaxNthDigits2();
        test.Print1ToMaxOfNDigits(3);
    }

}

猜你喜欢

转载自blog.csdn.net/weixin_37672169/article/details/80150580