面试题17------大数问题

面试题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 为正整数

题解:
大数越界问题: 当 n较大时,end=10^n - 1会超出 int32 整型的取值范围,超出取值范围的数字无法正常存储。但由于本题要求返回 int 类型数组如下第一个代码,返回vector<int>,即,数组中每一个元素都是int型),相当于默认规定了n的范围,给出n后,产生的所有数字都在 int32 整型取值范围内,因此不需要考虑大数越界问题,只需定义区间 [1, 10^n - 1]和步长 1 ,通过 for循环生成结果列表 res并返回即可。

class Solution {
    
    
public:
    vector<int> printNumbers(int n) {
    
    
       
    }
};

若本次不是用“一个整数列表来代替打印”,而是“直接打印出,且返回void类型”,就必须考虑大数问题

注:为练习大数问题,不管返回 int 类型数组还是void类型,都考虑大数解法。

一、简单解法:(不存在大数问题,这种方法可以)

class Solution {
    
    
public:
	vector<int> printNumbers(int n) {
    
    
		vector<int> res;
		if (n == 0) return res;
		//打印到数组中
		for (int i=1,max=pow(10,n);i<max;i++)
		{
    
    
			res.push_back(i);
		}
		return res;
	}
}; 

二、大数解法:(虽说本题返回int 类型数组,不存在大数问题)
在这一题中,由于返回的是一个 int 型的数组,所以是不可能超过INT_MAX的,但是一般大数问题都不会要求返回 int 数组来保存每一位数,而是循环输出每一位数

题解:
1、首先需要表达大数,常用的方法是:用字符串或数组表达大数。(这里用字符串)
2、将字符串的每一位初始化为'0'(字符),然后每一次为字符串表示的数字加1,再返回数组或打印出来。这里主要包括两步:
——2.1在字符串表达的数字上模拟加法
——2.2将字符串表达的数字以传统阅读的方式返回数组;
3、如何在每一次增加1后,快速判断是不是到了最大的n位数:
第一个字符产生进位时(即,最高位产生进位时)。
例如:n=3,只有当最大3位数999加1时,最高位才会产生进位。

知识点扩展:char与int之间的加减法(比如 1 + ‘0’ 和 ‘1’ - ‘0’)
首先我们看一下ASCII表,会发现红框部分是字符型型[0, 9]的ASCII值[48, 57],它们是连在一起的。
在这里插入图片描述
一个字符在内存中存放形式是以它的ASCII码形式存放的,所以
1、char与int相加,得到一个char。比如:‘0’ + 1 = 48 + 1 = 49 = ‘1’。
2、char减去char,得到一个int。比如:‘2’ - ‘0’ = 50 - 48 = 2。
所以,如果我们想把一个int型的数(比如 i = 5)转成char型,我们可以写成:char c = i + '0';。
如果我们想把一个char型的数(比如 c = '5')转成int型,我们可以写成:int i = c - '0'
【不管是int转换成char,还是cha转换成int,都需要'0'来辅助】

代码:

class Solution {
    
    
public:
    vector<int> output;
    vector<int> printNumbers(int n) {
    
    
        // 以下注释的前提:假设 n = 3
        if(n <= 0) return vector<int>(0);
        string s(n, '0'); // s最大会等于999,即s的长度为n
        while(!overflow(s)) inputNumbers(s);
        return output;
    }
    bool overflow(string& s)
    {
    
    
        // 本函数用于模拟数字的累加过程,并判断是否越界(即 999 + 1 = 1000,就是越界情况)
        //每次都只执行一次加1操作(即,if(i == s.length() - 1) current ++;),余下的循环是在处理个位加1后的进位操作;
        bool isOverFlow = false;
        int carry = 0; // carry表示进位
        for(int i=s.length()-1; i>=0; --i)
        {
    
    
            //s[i] - '0' :将s[i]对应的char转换成int型
            int current = s[i] - '0' + carry; // current表示执行新一轮操作之前s[i]的值
            if(i == s.length() - 1) current ++; // 如果i此时在个位,current执行 +1 操作; 加1操作只发生在个位。
            if(current >= 10)
            {
    
    
                // 假如i已经在最大的那一位了,而current++之后>=10,说明循环到头了,即999 + 1 = 1000
                if(i == 0) isOverFlow = true;
                else
                {
    
    
                    // 只是普通进位,比如current从9变成10
                    carry = 1;
                    s[i] = current - 10 + '0'; 
                }
            }
            else
            {
    
    
                // 如果没有进位,更新s[i]的值,然后直接跳出循环,这样就可以回去执行inputNumbers函数了,即往output里添加元素
                s[i] = current + '0';//current + '0' :将int型转换成char
                break;
            }
        }
        return isOverFlow;
    }
    void inputNumbers(string s)
    {
    
    
        // 本函数用于循环往output中添加符合传统阅读习惯的元素。比如001,我们会添加1而不是001。
        bool isUnwantedZero = true; // 判断是否是不需要添加的0,比如001前面的两个0
        string temp = "";
        for(int i=0; i<s.length(); ++i)
        {
    
    
            if(isUnwantedZero && s[i] != '0') isUnwantedZero = false;
            if(!isUnwantedZero) temp += s[i];
        }
        output.push_back(stoi(temp));//stoi:string to int把数字字符串转换成int输出
    }
};

知识点:

1、
stoi:参考

2、
解决大数问题的常用方法就是:
把数字转换成字符串。

猜你喜欢

转载自blog.csdn.net/qq_42647047/article/details/110523786
今日推荐