题目:
我们定义「顺次数」为:每一位上的数字都比前一位上的数字大 1 的整数。
请你返回由 [low, high] 范围内所有顺次数组成的 有序 列表(从小到大排序)。
示例 1:
输出:low = 100, high = 300
输出:[123,234]
示例 2:
输出:low = 1000, high = 13000
输出:[1234,2345,3456,4567,5678,6789,12345]
来源:
解题思路1:递归
拿到此题,第一反应是递归,拿回溯的代码套一下。你可能会问:为什么不叫回溯?此题中递归函数中只有1种可能,例如当前数字是12,那么下一个数字必定是123,而不是124,所以不存在回溯的情况。
使用递归需要注意的地方:
- 结果无序,需要排序后输出
- 注意数字达到10时的处理
class Solution {
public:
vector<int> result;
vector<int> sequentialDigits(int low, int high) {
for (int i = 1; i < 10; i++) {
int path = i;
back(path, i+1, low, high);
}
sort(result.begin(), result.end());
return result;
}
void back(int path, int next, int low, int high) {
if (path >= low) {
result.push_back(path);
}
if (next == 10) return;
path = path * 10 + next;
if (path <= high) {
back(path, next+1, low, high);
}
}
};
解题思路2:一种取巧的思路
事先定义所有的顺次数,遍历即可,代码简单不做解释。
class Solution {
public:
vector<int> sequentialDigits(int low, int high) {
vector<int> result;
int nums[36] = {
12,23,34,45,56,67,78,89,
123,234,345,456,567,678,789,
1234,2345,3456,4567,5678,6789,
12345,23456,34567,45678,56789,
123456,234567,345678,456789,
1234567,2345678,3456789,
12345678,23456789,
123456789};
for (int i = 0; i < 36; i++) {
if (nums[i] >= low && nums[i] <= high) {
result.push_back(nums[i]);
}
}
return result;
}
};
解题思路3:规律
根据思路2得出,nums从第一行开始,数字宽度依次是2,3,4,5,6,7,8,9。定义一个变量width,记录数字的宽度,可以从2开始,也可以从数字low的宽度开始。
在相同宽度下,滑动数字窗口,根据当前数字得出下一个数字,如何计算,下面举例说明:
- 在width=3的情况下,当前数字如果是234,那么下一个数字是:(2345) % 1000 = 345。
- 在width=5的情况下,当前数字如果是23456,那么下一个数字是:(234567) % 100000 = 34567。
定义一个变量mod记录上面的1000, 100000,每当width+1时,mod = mod*10。
如果当前数字>high,遍历结束。
class Solution {
public:
vector<int> sequentialDigits(int low, int high) {
vector<int> result;
int width = 0; // 位数
int t = low;
long mod = 1;
while (t > 0) {
width++;
t /= 10;
mod *= 10;
}
while (width < 10) {
bool end = false;
long sum = 0; // width组成的数字
int n = 1;
for (; n < width; n++) sum = sum * 10 + n; // 前width-1组成的数字
for (; n < 10; n++) {
sum = (sum * 10 + n) % mod;
if (sum > high) {
end = true;
break;
}
if (sum >= low) {
result.push_back(sum);
}
}
if (end) break;
width++;
mod *= 10;
}
return result;
}
};
总结:
本人更倾向于思路1和2,思路2不必说,思路1可以根据回溯的套路写,较容易实现。
思路3没有套路可循,需要反复调试才能通过,也许它的运行效率较高,但实现复杂、编写耗时,不推荐。