实现一个十进制数字报数程序,请按照数字从小到大的顺序返回一个整数数列,该数列从数字 1 开始,到最大的正整数 cnt 位数字结束。
示例 1:
输入:cnt = 2
输出:[1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99]
- 很容易想到的是,cnt 为几代表从 1 到 10的cnt次-1,所以可以直接循环暴力
-
public int[] countNumbers(int cnt) { int end = (int)Math.pow(10,cnt)-1; ans = new int[end]; for(int i=0;i<end;i++){ ans[i] = i+1; } return ans; }
- 他人题解:那这题肯定不是为了考察你对循环的使用吧,你用 short/int/long 存储数字,都终归有个存储范围,因此表示大数时考虑使用字符串类型。使用字符串的话,其实你会发现这就是 cnt 位的 0-9 的全排列问题。由于每位都是一样的操作,所以我们基于分治的思想递归生成结果。即遍历 0-9 ,先固定最高位,次高位,…最后固定最后一位,最后一遍遍历到下个数,然后回溯到次低位遍历到下个数…比如 cnt 为 3,先固定第一位 0,然后固定第二位 0,然后第三位的 0,得到 000,继续遍历最后一位,得到 001,002…009,最后一位遍历结束,向上回溯遍历第二位得到 010,011…,初步编写全排列代码如下
-
StringBuilder res; int count = 0, cnt; char[] num, loop = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9'}; public String countNumbers(int cnt) { this.cnt = cnt; res = new StringBuilder(); // 数字字符串集 num = new char[cnt]; // 定义长度为 cnt 的字符列表 dfs(0); // 开启全排列递归 res.deleteCharAt(res.length() - 1); // 删除最后多余的逗号 return res.toString(); // 转化为字符串并返回 } void dfs(int x) { if(x == cnt) { // 终止条件:已固定完所有位 res.append(String.valueOf(num) + ","); // 拼接 num 并添加至 res 尾部,使用逗号隔开 return; } for(char i : loop) { // 遍历 ‘0‘ - ’9‘ num[x] = i; // 固定第 x 位为 i dfs(x + 1); // 开启固定第 x + 1 位 } }
- 这里可以生成我们要的结果,但是会多出比如 000,010,020 这种高位补 0 的字符串,因此第一步我们先去掉高位的 0,我们用一个 start 表示结果字符串 str 的左边界,比如 cnt 为 3,最开始的 000-009,其实我们只要取第 3 位即可,之后的 010-099,其实我们只要取第二位到第三位,start 对应的分别是 str[2:],以及 str[1:],即左边界 start 随着临界值 9,99,999… 不断减少 1,初始为 cnt-1(看代码能看懂),为了不断更新 start,我们就需要用一个变量比如 nine 来表示当前数包含的 9 的数量 ,这就能表示是否来到了临界值。观察后我们会得到如果 cnt-start==nine(cnt-start 你可以看做几位数,比如等于 1 为表示一位数,等于 2 则为表示两位数,nine 就是上面说的,表示增加位数的临界点),start 就需要减 1 了。你可以理解为比如当 nine 为 1,即之后的最大值开始为两位数了,start 每减少 1 就是能表示的数的最多位 +1,所以我们所能表示的就从最开始的一位数成了两位数。
- 还剩最后一个问题,即题目要求从 1 开始,而我们是从 0 开始,很简单,比如最开始的 000,我们得到了一位数 0,为 0 我们就跳过呗
- 这里的 nine 的获取别忘了恢复
-
int[] res; // nine:当前数包含的 9 的个数 int nine = 0, count = 0, start, cnt; // num:表示结果字符串的 char 数组 char[] num, loop = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9'}; public int[] countNumbers(int cnt) { this.cnt = cnt; res = new int[(int)Math.pow(10, cnt) - 1]; num = new char[cnt]; start = cnt - 1; dfs(0); return res; } void dfs(int x) { if(x == cnt) { String s = String.valueOf(num).substring(start); if(!s.equals("0")) res[count++] = Integer.parseInt(s); if(cnt - start == nine) start--; return; } for(char i : loop) { if(i == '9') nine++; num[x] = i; dfs(x + 1); } // 回溯后没 9 就给我恢复了,有的话我自会加 nine--; }