c++ - 将递归算法生成所有组合转换为迭代
问题描述
谁能帮我把这个算法变成一个迭代算法。我知道递归只是迭代加上一个堆栈,但到目前为止我还没有想出一个合适的解决方案。
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;
}
解决方案
我不会给你答案,但我会给你一个关键技巧,让你可以合理地机械地做到这一点。
你的心理障碍的一部分是你有两种类型的控制流交织在一起。第一个是你的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
:检查你的 if,决定你是否会有一个循环,然后你是否将它设置为开始并附加到ENTER_LOOP
你的动作堆栈上。(如果您不循环,则执行 if。)ENTER_LOOP
:测试循环条件。如果匹配,则设置循环,并将ENTER_LOOP
,EXIT_LOOP
,EXIT_FUNCTION
,附加ENTER FUNCTION
到操作堆栈上。(请注意,堆栈中的最后一项将首先发生。)然后将函数调用的参数添加到调用堆栈上,以便在进行递归调用时它们就在那里。EXIT_LOOP
:执行chosen.pop_back()
并增加i
当前调用框架的一部分。(这很重要,调用帧需要分开!)EXIT_FUNCTION
: 去掉顶部的调用框架,就完成了。
一旦你认为你理解了这个策略,就去学习Forth。:-)
推荐阅读
- angular - “异或”中的 FormGroup 验证
- c# - BlockingCollection 每组多个消费者 FIFO
- google-apps-script - 在 appscript 中使用 OAUTH2.0 进行身份验证的最可行方法是什么?
- javascript - 如何优雅地将对象剥离到特定字段?
- html - 连接 Vue.js 和 laravel
- spring-ws - Spring WebServiceTemplate URL
- javascript - 将 IF/Else 条件转换为 Ramda Cond 失败
- python - 为什么我收到 IndexError: list index out of range 消息?
- bash - 在 bash 脚本中运行多个命令
- ios - Xcode 的 SceneKit 编辑器渲染 .dae 文件错误,Xcode 有时在预览期间崩溃