根据我的研究,我知道这应该是一个递归解决方案
一点也不。
请注意,如果您没有不必要地使用递归,则可以避免某些潜在问题(递归级别和每级堆栈增长),并且通常更容易理解代码。
让我们看看你在做什么。如果我们暂时忽略负数,您基本上会生成以下序列(对于 k=2,n=4):
0 0 0 0 0 1 0 0 0 2 0 0
0 0 0 1 0 1 0 1 0 2 0 1
0 0 0 2 0 1 0 2 0 2 0 2
0 0 1 0 0 1 1 0 0 2 1 0
0 0 1 1 0 1 1 1 0 2 1 1
0 0 1 2 0 1 1 2 0 2 1 2
0 0 2 0 0 1 2 0 0 2 2 0
0 0 2 1 0 1 2 1 0 2 2 1
0 0 2 2 0 1 2 2 0 2 2 2
如果 k 是 9,那么您只需按十进制计数。在我见过的所有学习数数的孩子中,我从来不知道有人会使用递归来数数。 ;) 如果你退后一步想想你是如何学会计算大数的,你应该会找到一个更简单的解决方案......但我会留到以后。
如果你用二进制计算,它会如下所示:
0 = 000
1 = 001
2 = 010
3 = 011
4 = 100
5 = 101
6 = 110
7 = 111
或用k=1 和n=3 计数(使用-k 到k):
0 = -1 -1 -1 9 = 0 -1 -1 18 = 1 -1 -1
1 = -1 -1 0 10 = 0 -1 0 19 = 1 -1 0
2 = -1 -1 1 11 = 0 -1 1 20 = 1 -1 1
3 = -1 0 -1 12 = 0 0 -1 21 = 1 0 -1
4 = -1 0 0 13 = 0 0 0 22 = 1 0 0
5 = -1 0 1 14 = 0 0 1 23 = 1 0 1
6 = -1 1 -1 15 = 0 1 -1 24 = 1 1 -1
7 = -1 1 0 16 = 0 1 0 25 = 1 1 0
8 = -1 1 1 17 = 0 1 1 26 = 1 1 1
因此,如果您喜欢冒险,您可以:
- 轻松计算要输出的排列数
- 使用简单的循环在范围内循环
- 将每个数字转换为基数 k*2+1
- 通过减去 k 来偏移每个数字
当然还有前面提到的更简单的方法。在下面的代码中调用Counter(k, nest_level);。 (后解释)
void WriteVector(const std::vector<int>& v)
{
for (const auto i : v)
std::cout << i << " ";
std::cout << std::endl;
}
bool VectorInc(const int k, std::vector<int>& v)
{
for (auto it = v.rbegin(); it != v.rend(); it++)
{
if ((*it) < k) {
(*it)++;
return true;
}
(*it) = -k;
}
return false;
}
void Counter(const int k, const int n)
{
std::vector<int> v(n, -k);
WriteVector(v);
while (VectorInc(k, v))
WriteVector(v);
}
-
Counter 用size == nest_level 初始化一个向量,每个元素都包含-k。
- 在循环中调用
VectorInc 来模拟加1(或计数)。
-
VectorInc 是一个非常简单的函数,只要它需要执行“结转”,它就会从右到左循环遍历向量元素。
- 它通过添加 1 来更新当前元素。
- 但如果当前元素最大为
k,它应该回滚到-k,并且“结转”+1 到左边的数字。
- 当向量最终到达
{k, k, k, ..., k} 时,将每个数字加 1 回滚到 -k 和 VectorInc 返回 false,表示存在溢出。
优点:简单,无递归,几乎可以使用任何 k & n 值。
缺点:几乎可以使用任何值k&n。 尝试像Counter(10, 80) 这样看似无害的调用,您将花费很长时间等待您的程序计算宇宙中的原子。