题目链接:https://leetcode.cn/problems/da-yin-cong-1dao-zui-da-de-nwei-shu-lcof/
1. 题目介绍(17. 打印从1到最大的n位数)
输入数字 n,按顺序打印出从 1 到最大的 n 位十进制数。比如输入 3,则打印出 1、2、3 一直到最大的 3 位数 999。
【测试用例】:
输入: n = 1
输出: [1,2,3,4,5,6,7,8,9]
【条件约束】:
说明:
- 用返回一个整数列表来代替打印
- n 为正整数
2. 题解
2.1 普通循环 – O(10n)
时间复杂度O(10n),空间复杂度O(10n)
class Solution {
// 方法1:不考虑数据溢出的情况(大数问题)
public int[] printNumbers(int n) {
// 1. 确定要打印的数字范围
int num = 1;
while (n > 0){
num *= 10;
n--;
}
// 2. 定义满足数字范围的数组
int[] arr = new int[num-1];
// 3. 循环遍历,将数字依次存入数组
for (int i = 0; i < num-1; i++){
arr[i] = i+1;
}
// 4. 返回数组
return arr;
}
}
可简化为:
class Solution {
public int[] printNumbers(int n) {
int end = (int)Math.pow(10, n) - 1;
int[] res = new int[end];
for(int i = 0; i < end; i++)
res[i] = i + 1;
return res;
}
}
2.2 大数求解 – O(10n)
时间复杂度O(10n),空间复杂度O(10n)
- 我们通过字符数组
num
来存储当前数字- 通过整型数组
ans
来存储应打印的所有数字- 在递归方法
dfs
中,一旦当前长度==输入值n时,就将其从字符数组转化成字符串。由于本题要求输出 int 类型数组,故为运行通过,还要将其再转化为 int 类型。- dfs中的for循环+递归,就是为了填充字符数组num。以输入值n=2为例,最后一个数字99,num[0]=‘9’,dfs(1) -> num[1] = ‘9’,n==2 条件满足,转化转化,最后存入数组。
class Solution {
char[] num;
int[] ans;
int count = 0,n;
public int[] printNumbers(int n) { // n = 2
this.n = n;
num = new char[n];
ans = new int[(int) (Math.pow(10, n) - 1)];
dfs(0);
return ans;
}
private void dfs(int n) { // 0
if (n == this.n) {
// 将字符数组num转成字符串
String tmp = String.valueOf(num);
// 将字符串转成int
int curNum = Integer.parseInt(tmp);
// 存入数组
if (curNum!=0) ans[count++] = curNum;
return;
}
for (char i = '0'; i <= '9'; i++) {
num[n] = i;
dfs(n + 1);
}
}
}
3. 扩展
3.1 本题扩展
【本题扩展】:在前面的代码中,我们都是用一个char型字符表示十进制的数字的一位。8个bit的char型字符最多能表示256个字符,而十进制数字只有0~9的10个数字。因此用char型字符串表示十进制的数字并没有充分利用内存,有一些浪费,有没有更高效的方式来表示大数?
答案:位图(BitMap)
在一些数据量比较大的场景中,做一些查重、排序,一般的方法难以实现。数据量过大,会占用较大的内存,常用的处理方式有两种:BitMap
(位图法)和布隆过滤
。
所谓BitMap就是用一个bit位来标记某个元素对应的value,而key即是这个元素。由于采用bit为单位来存储数据,因此在可以大大的节省存储空间。
BitMap 优缺点:
- 优点:实现简单。适合数据量比较大的场景。
- 缺点:占用内存。申请的数组长度不好控制和最大的数值有关。当某个值特别大的情况下,映射的数组超过申请的数组容量,会出现下标越界。
3.2 相关题目
【相关题目】:定义一个函数。在该函数中可以实现任意两个整数的加法。由于没有限定输入两个数的大小范围,我们也要把它当做大数问题来处理,在前面的代码的第一个思路中,实现了在字符串表示的数字上加1的功能,我们可以参考这个思路实现两个数字的相加功能。另外还有一个需要注意的问题:如果输入的数字中有负数,我们应该怎么处理?
可参考:定义一个函数,在该函数中可以实现任意两个整数的加法。java实现
题解思想:
对于这道题,由于没有限定输入的两个数的范围,我们要按照大数问题来处理。由于题目是要求实现任意两个整数的加法,我们就要考虑如何实现大数的加法。此外这两个整数是任意的,所以也有可能存在负数。通常对于大数问题,常用的方法就是使用字符串来表示这个大数。我们可以首先将两个整数分别用字符串来表示,然后分别将这两个字符串拆分成对应的字符数组。当两个整数都是正数的时候直接相加结果为正数,同为负数的时候取两者的绝对值相加然后在结果前加一个负号。假若是一正一负,则用两者的绝对值相减,用绝对值大的数减去绝对值小的数,当正数的绝对值大的时候相减的结果为正数,当负数的绝对值大的时候相减的结果为负数,结果为负数时在相减的结果前加一个负号即可。在具体进行相加的时候两个字符数组对应的数字字符相加即可,当有进位的时候做出标记,在更高一位进行相加时再将这个进位加进去。同样在相减的时候有借位的也做出标记,在更高一位相减的时候将这个借位算进去。
4. 参考资料
[1] 面试题17. 打印从 1 到最大的 n 位数(分治算法 / 全排列,清晰图解)
[2] java基础–BitMap
[3] java中BitMap实现
[4] 第17题:打印1到最大的n位数
[5] 【转载】经典算法系列之(一) - BitMap