• 剑指Offer-打印从 1 到最大的 n 位数


    题目

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

    解法探析

    解法 1

    看完这道题目之后,我们一般都会想到先求出最大的 n 位数,再用一个循环从 1 开始逐个打印。代码如下所示:

    int Print1ToMaxOfNDigits(int n)
    {
        int number = 1;
        int i = 0;
        
        while (i++ < n) {
            // 求出最大的 n 位数
            number *= 10;
        }
    
        for (i = 1; i < number; ++i) {
            printf("%d	", i);
        }
    
        return 0;
    }
    

    仔细分析会发现题目中没有规定 n 的范围,当 n 很大时,代码中不管是用整型(int)或长整形(long long)最终都会溢出,故这里就要考虑一下大数处理的问题。

    解法 2

    对于大数问题的处理,我们一般能想到的方法是用字符串或者数组。

    Print1ToMaxOfNDigits函数

    int Print1ToMaxOfNDigits(int n)
    {   
        // n 小于等于 0 时不符合条件,则异常退出
        if (n <= 0) {
            return 1;
        }
    
        // 给表示数字的字符串 number 申请 n + 1 个char类型大小的内存空间
        char* number = (char*)malloc(sizeof(char) * (n + 1));
    
        if (number == NULL) {               // 若内存申请失败,则异常退出
            return 1;
        }
    
        memset(number, '0', n);             // 将字符串中的每一个数字初始化为 '0'
        number[n] = '';                   // 字符串的最后一位初始化为结束符号 ''
    
        while (!Increment(number)) {        // 是否已经加到最大的数字,若不是,继续打印;若是,停止打印
            PrintNumber(number);
        }
    
        free(number);                       // 释放内存
        number = NULL;                      // 将指针置 NULL,防止“野指针”
    
        return 0;
    }
    

    Increment函数

    函数 Increment 实现在表示数字的字符串 number 上增加 1。如果做加法溢出,则返回 true,否则为 false。

    bool Increment(char* number)
    {
        bool isOverFlow = false;            // 是否超过最大的 n 位数
        int takeOver = 0;                   // 进位
        int length = strlen(number);
    
        for (int i = length - 1; i >= 0; i--) {
            // 把字符数字转换为数字,再加上进位
            int Sum = number[i] - '0' + takeOver;
    
            if (i == length - 1) {          // 若 i 是字符串最后一位字符数组,直接 Sum++
                Sum++;
            }
    
            if (Sum == 10) {
                if (i == 0) {               // 若 i 是字符串首位
                    isOverFlow = true;      // 达到了最大数字,以 n = 3 为例,此时 number = 1099,i 下标对应的字符是 10
                } else {                    // 若 i 不是字符串首位
                    Sum -= 10;
                    takeOver = 1;
                    number[i] = '0' + Sum;
                }
            } else {                        // 若Sum++ 之后 Sum < 10
                number[i] = '0' + Sum;      // 把数字转换为字符存入数组中
                break;                      // 跳出本层循环
            }        
        }
    
        return isOverFlow;
    }
    

    C99标准里已经定义了bool类型,在C99及以上标准中想使用bool(布尔)类型,只需引入 <stdbool.h> 头文件即可。

    PrintNumber函数

    当数字不够 n 位时,在数字前面补 0,而打印数字时,这些补位的 0 不应该打印出来,应该在碰到第一个非 0 的字符之后开始打印,直至字符串的结尾。

    int PrintNumber(char* number)
    {
        // isBeginning0 为 true 表示字符串是以 0 开头的
        bool isBeginning0 = true;                   
        int length = strlen(number);
    
        for (int i = 0; i < length; ++i) {
            if (isBeginning0 && number[i] != '0') {
                isBeginning0 = false;
            }
    
            // 当字符串的字符为非 0 时才开始打印
            if (!isBeginning0) {
                printf("%c", number[i]);
            }
        }
    
        printf("	");
    
        return 0;
    }
    

    解法 3

    如果在数字前面补 0,就会发现 n 位所有十进制数其实是 n 个从 0 到 9 的全排列。我们只要把数字的每一位从 0 到 9 排列一遍,就能得到所有的十进制数。

    Print1ToMaxOfNDigits函数

    int Print1ToMaxOfNDigits(int n)
    {
        // n 小于等于 0 时不符合条件,则异常退出
        if (n <= 0) {
            return 1;
        }
    
        // 给表示数字的字符串 number 申请 n + 1 个char类型大小的内存空间
        char* number = (char*)malloc(sizeof(char) * (n + 1));
    
        if (number == NULL) {               // 若内存申请失败,则异常退出
            return 1;
        }
    
        memset(number, '0', n);             // 将字符串中的每一个数字初始化为 '0'
        number[n] = '';                   // 字符串的最后一位初始化为结束符号 ''
    
        for (int i = 0; i < 10; ++i) {
            number[0] = i + '0';
            Print1ToMaxOfNDigitsRecursively(number, n, 0);
        }
    
        free(number);                       // 释放内存
        number = NULL;                      // 将指针置NULL,防止“野指针”
    
        return 0;
    }
    

    Print1ToMaxOfNDigitsRecursively函数

    全排列用递归很容易实现,数字的每一位都可能是 0~9 中的某一个数,然后设置下一位。递归结束的条件是已经设置了数字的最后一位。

    void Print1ToMaxOfNDigitsRecursively(char* number, int length, int index)
    {
        if (index == length - 1) {
            PrintNumber(number);
            return;
        }
    
        for (int i = 0; i < 10; ++i) {
            number[index + 1] = i + '0';
            Print1ToMaxOfNDigitsRecursively(number, length, index + 1);
        }
    }
    

    函数 PrintNumber 参见 解法 2 中的 PrintNumber 函数。


    个人主页:

    www.codeapes.cn

  • 相关阅读:
    SQL Server-数据库架构和对象、定义数据完整性
    SQL Server 2014 中,新建登录用户,分配权限,并指定该用户的数据
    SQL Server SQL性能优化之--数据库在“简单”参数化模式下,自动参数化SQL带来的问题
    SQL Server-简单查询语句,疑惑篇
    SQL Server-聚焦聚集索引对非聚集索引的影响
    SQL Server-聚焦使用索引和查询执行计划
    SQL Server-聚焦移除Bookmark Lookup、RID Lookup、Key Lookup提高SQL查询性能
    SQL SERVER中的sys.objects和sysobjects的区别
    详解sqlserver查询表索引
    双系统如何正确的使用修复BCD工具分享
  • 原文地址:https://www.cnblogs.com/codeapes666/p/12251655.html
Copyright © 2020-2023  润新知