任何人都可以帮助我将此算法转换为迭代算法。我知道递归只是迭代加上堆栈,但是到目前为止我还没有想出合适的解决方案。
void rGenCombs(int n, int k, vector<int>& chosen, int idx,
vector<vector<int>>& combs) {
if (chosen.size() == k) {
combs.push_back(chosen);
return;
}
for (int i = idx; i <= n; ++i) {
chosen.push_back(i);
rGenCombs(n, k, chosen, i + 1, combs);
chosen.pop_back();
}
}
vector<vector<int>> genCombsRec(int n, int k) {
vector<vector<int>> combs;
vector<int> chosen;
rGenCombs(n, k, chosen, 1, combs);
return combs;
}
更新,我现在有这个。问题是我不知道该写哪个循环。我猜应该可以通过一个简单的while循环以某种方式实现。
vector<vector<int>> genCombs(int n, int k) {
vector<int> numStack, chosen;
vector<vector<int>> combs;
numStack.push_back(1);
while (!numStack.empty()) {
if (chosen.size() == k) {
combs.push_back(chosen);
chosen.pop_back();
continue;
}
chosen.push_back(numStack.back());
if (numStack.back() <= n) {
numStack.push_back(numStack.back() + 1);
} else {
numStack.pop_back();
}
}
return combs;
}
解决方案
对于不需要堆栈的其他迭代算法,我得出以下结论:
int getNextIncIndex(const vector<int>& combs, int n) {
int k = static_cast<int>(combs.size());
for (int i = k - 1; i >= 0; --i) {
int distFromRight = k - i - 1;
if (combs[i] < n - distFromRight) {
return i;
}
}
return -1;
}
vector<vector<int>> genCombs(int n, int k) {
vector<vector<int>> combs;
vector<int> comb(k, 1);
iota(comb.begin(), comb.end(), 1);
while (true) {
for (int i = comb[k - 1]; i <= n ; ++i) {
comb[k - 1] = i;
combs.push_back(comb);
}
int incIdx = getNextIncIndex(comb, n);
if (incIdx == -1) {
break;
} else {
iota(comb.begin() + incIdx, comb.end(), comb[incIdx] + 1);
}
}
return combs;
}
答案 0 :(得分:2)
我不会给你答案,但是我会给你关键技巧,以合理地机械地完成它。
您的智力障碍的一部分是,您有两种类型的控制交织在一起。第一个是您的for
循环。第二个是递归。如何从for
循环的内部跳到外部循环并递归,而又回到for
循环的内部?很容易感到困惑。
但是引入的不是一个堆栈,而是两个 堆栈。一个堆栈是用来跟踪您需要执行的操作。另一个用于呼叫帧中的所有数据。最终代码的关键部分如下所示:
while (0 < actions.size()) {
action thisAction = actions.pop_back();
switch (thisAction.type) {
case ENTER_FUNCTION:
...
break;
case ENTER_LOOP:
...
break;
case EXIT_LOOP:
...
break;
case EXIT_FUNCTION:
...
break;
}
}
现在,您以统一的方式跟踪循环和函数调用。不再混乱。
这是您在每个部分中要做的事情。
ENTER_FUNCTION
:检查是否存在,确定是否有循环,然后将其设置为开始并将ENTER_LOOP
附加在操作堆栈上。 (如果您不会循环播放,请执行if。)ENTER_LOOP
:测试循环条件。如果匹配,则建立循环,并在操作堆栈上附加ENTER_LOOP
,EXIT_LOOP
,EXIT_FUNCTION
,ENTER FUNCTION
。 (请注意,堆栈中的最后一项将首先发生。)然后在调用堆栈上添加函数调用的参数,以便在进行递归调用时它们就在那里。EXIT_LOOP
:执行chosen.pop_back()
并增加当前调用框架中的i
。 (这很重要,呼叫帧必须分开放置!)EXIT_FUNCTION
:摆脱掉最前面的调用框架,它就完成了。一旦您认为自己了解这种策略,就去学习Forth。 :-)
答案 1 :(得分:1)
如果您只需要迭代算法,我认为您的方向错误。真的不需要堆栈。
如果出于任何原因要做想要堆放,请忽略其余部分。
出于说明目的,我使用n=6, k=3
运行您的代码:
1 2 3
1 2 4
1 2 5
1 2 6
1 3 4
1 3 5
1 3 6
1 4 5
1 4 6
1 5 6
2 3 4
2 3 5
2 3 6
2 4 5
2 4 6
2 5 6
3 4 5
3 4 6
3 5 6
4 5 6
您会看到一个简单的模式,它导致了简单的算法:
采取最后一个位置。增加它。这将给出下一个组合。
一旦达到最高点,即该位置不能再增加,请增加下一个“可递增”位置,并std::iota
移到右边。
重新开始,继续前进直到不再有可递增的位置。
一个非常肮脏但可行的实现,还有很大的提升空间:
#include <numeric>
int find_incrementable(std::vector<int>& current, int n)
{
int pos;
current.push_back(n + 1); // Dirty hack
for (pos = current.size() - 2; pos >= 0; --pos) {
if (current[pos] + 1 < current[pos + 1]) {
break;
}
}
current.pop_back();
return pos;
}
std::vector<std::vector<int>> genCombsIter(int n, int k)
{
std::vector<std::vector<int>> combs;
std::vector<int> current(k);
std::iota(current.begin(), current.end(), 1);
combs.push_back(current);
int position = k - 1;
int incrementable;
while ((incrementable = find_incrementable(current, n)) >= 0) {
if (incrementable == position) {
current[position] += 1;
} else {
if (incrementable == -1) {
break;
}
std::iota(current.begin() + incrementable, current.end(), current[incrementable] + 1);
position = k - 1;
}
combs.push_back(current);
}
return combs;
}