C++-如何增加堆栈大小以允许Kosaraju算法进行更多递归以计算强连接组件
C++- How to increase stack size to allow more recursion for Kosaraju's Algorithm to compute Strongly Connected Components
我使用的是mac、4GB RAM和CLion IDE。编译器是Clang。我需要在深度优先搜索的递归实现中允许更多的递归(目前在具有80k个节点的图上失败(。
typedef unordered_map <int, vector<int>> graph;
void DFS (graph &G, int i, vector <bool> &visited) {
visited[i] = true;
for (int j = 0; i < G[i].size(); j++) {
if (!visited[G[i][j]]) {
DFS(G, G[i][j], visited);
}
}
t++;
finishingTime[t] = i; //important step
}
这是Kosaraju算法的一个实现,用于计算图中的强连通分量。https://en.wikipedia.org/wiki/Kosaraju%27s_algorithm我知道可以将DFS实现为迭代,但最后一步很重要,而且我找不到使用迭代来包含它的方法。这是因为当DFS失败并发生回溯时,就会执行该步骤,而递归提供了一种非常自然的方法。
所以目前我只有两个选择:
- 增加堆栈大小以允许更多递归
- 或者找到迭代解决方案
有什么办法吗?
根据注释的建议,您可以将对DFS的每次调用放在根据DFS参数列表分配的堆上的堆栈上,然后在堆栈中迭代。堆栈中的每个条目本质上都是一个任务。
类伪代码:
Start and run "recursion":
nr_of_recursions = 0;
dfs_task_stack.push(first_task_params)
while dfs_task_stack not empty
DFS(dfs_task_stack.pop)
nr_of_recursions += 1
end while;
true_finishingtime[] = nr_of_recursions - finishingtime[];
DFS:
for each recursion found
dfs_task_stack.push(task_params)
end for;
t++; finishingtime...
不确定你的算法,但你将任务推到堆栈的顺序可能很重要,即"针对每个…"的顺序。
我冒昧地将"结束时间"的含义重新定义为相反的含义。要获得原始定义,请将新的finishingtime减去递归的总数。
我不知道这是否是最好的解决方案,但您可以通过拥有多个访问状态,只使用一个堆栈和一个访问状态数组来构建完成时间列表。
下面的代码只是为了说明算法。我实际上并没有对它进行太多测试(只是一个{{0, {1}}, {1, <>}, {2, <>}}
小测试(,但我过去已经以同样的方式对更大的图使用了这种技术,我知道它是有效的。
其想法是在访问节点之后将其保留在堆栈中,直到在弹出之前访问完所有节点,从而模拟递归调用,但在堆栈对象中推送的数据较少。
#include <iostream>
#include <vector>
#include <stack>
#include <cassert>
#include <unordered_map>
using namespace std;
typedef enum {
vssClean,
vssPushed,
vssVisited
} VerticeStackState;
typedef unordered_map <int, vector<int>> graph;
void kosarajuBuildFinishOrder(const int inital, graph &G, vector<int> &finish, vector<VerticeStackState> &state, int &lastFinished) {
assert(vssClean == state[inital]);
std::stack<int> stack;
stack.push(inital);
state[inital] = vssPushed;
int current;
while (!stack.empty())
{
current = stack.top();
if (vssPushed == state[current])
{
state[current] = vssVisited;
for (const auto to: G[current])
{
if (state[to]==vssClean)
{
state[to] = vssPushed;
stack.push(to);
}
}
}
else {
assert(vssVisited == state[current]);
stack.pop();
finish[--lastFinished] = current;
}
}
}
int main() {
graph G;
G.insert({0, vector<int>(1, 1)});
G.insert({1, vector<int>()});
G.insert({2, vector<int>()});
vector<int> finish(G.size(), 0);
vector <VerticeStackState> state(G.size(), vssClean);
int lastFinished = G.size();
for (int i=0; i < G.size(); ++i) {
if (vssClean == state[i]){
kosarajuBuildFinishOrder(i, G, finish, state, lastFinished);
}
}
for (auto i: finish) {
cout << i << " ";
}
return 0;
}
对于您提到的关于增加堆栈大小的选项之一,您可以这样做:
g++ -Wl,--stack,16777216 -o kosaraju.exe kosaraju_stl.cpp
这将使堆栈大小增加到16MiB。正如前面的回答所提到的,这只是推迟了问题的解决。
typedef unordered_map <int, vector<int>> graph;
void DFS (graph &G, vector <bool> &visited) {
std::stack<int> stack;
stack.push(0); // root
int i, j;
while(!stack.empty())
{
i = stack.pop_back();
visited[i] = true;
for (j= (int) G[i].size() -1; j >= 0; j--)
{
if (!visited[G[i][j]])
{
stack.push_back(G[i][j]);
}
}
t++;
finishingTime[t] = i; //important step
} // end while.
}
任何人都可能犯编程错误,因为我没有你的测试数据,所以我不能测试这个,但输出是一样的吗?
- 到连接组件算法的问题(递归)
- 需要为 C++ 中的以下问题设计递归算法
- 这种用于查找连续子数组中最大和的递归算法有什么优势吗?
- 给出不精确答案的递归Karatsuba算法
- 运行合并排序递归算法时EXC_BAD_ACCESS错误
- 如何改进搜索二项式系数的递归算法
- 如何转换多次调用自己的递归算法?
- 如何从算法中找到递归关系
- 递归合并排序算法实现
- 如何将字符串保存在最长的常见子序列递归算法中
- 这是河内算法的递归塔是一种不知情的搜索
- 编写递归算法以从链表中删除元素.编写递归算法以将元素添加到链表中
- 了解一种神秘地起作用的递归二进制搜索算法
- 指针似乎迷失在递归算法中
- 为什么我的递归快速排序算法有如此不平衡的分区
- 为什么这种递归子集和算法会导致指针分配错误
- 如何从递归算法返回节点
- C++中的斐波那西算法 - 递归
- 最长公共子序列算法递归解的记忆
- 快速排序算法(递归)