面试题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、
解决大数问题的常用方法就是:
把数字转换成字符串。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
java面试题真的很多,下面我来回答一个有关多线程的问题。 在Java中实现多线程有两种方式,一种是继承Thread类,另一种是实现Runnable接口。这两种方式有何区别? 继承Thread类的方式是直接定义一个类继承Thread,并重写它的run()方法。然后创建该类的对象,并调用对象的start()方法来启动线程。这种方式简单直接,但因为Java是单继承的,所以如果某个类已经继承了其他类,就不能再直接继承Thread类实现多线程。 实现Runnable接口的方式是定义一个类实现Runnable接口,并实现其唯一的抽象方法run()。然后创建Thread类的对象,将实现了Runnable的对象作为参数传递给Thread类的构造方法。最后调用Thread对象的start()方法来启动线程。这种方式灵活性更大,因为Java允许一个类实现多个接口,所以即使某个类已经继承了其他类,仍然可以通过实现Runnable接口来实现多线程。 另一个区别在于资源共享的问题。继承Thread类的方式,不管是数据还是方法,都是线程自己拥有的,不存在共享的情况。而实现Runnable接口的方式,多个线程可以共享同一个对象的数据和方法,因为多个线程共同操作的是同一个Runnable对象。 总结来说,继承Thread类方式简单直接,但只能通过单继承来实现多线程;实现Runnable接口方式更灵活,可以解决单继承的限制,并且多个线程可以共享同一个Runnable对象的数据和方法。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值