gpt4 book ai didi

java - 将递归方法(递归在循环内完成)转换为迭代方法

转载 作者:行者123 更新时间:2023-11-30 03:35:19 27 4
gpt4 key购买 nike

我有一个递归算法,用于迭代分层数据结构,但不幸的是,对于某些数据,分层结构太深了,以至于我收到了 StackOverflowError。我已经看到这种情况发生在深度约为 150 个节点的情况下,而数据可能会增长得比这更远。对于上下文,此代码将在有限的环境中运行,并且无法更改 JVM 堆栈大小,并且数据结构是给定的并代表具有目录和文件的不同文件系统。

为了解决堆栈溢出问题,我尝试将算法转换为迭代算法。这不是我以前必须做的事情,所以我从一些示例开始,展示如何通过简单的递归来做到这一点,但我不确定如何将其应用于循环内的递归。我找到了一种似乎可行的方法,但代码相当疯狂。

这是我原始递归方法的简化版本:

private CacheEntry sumUpAndCacheChildren(Node node) {
final CacheEntry entry = getCacheEntry(node);

if (entryIsValid(entry))
return entry;

Node[] children = node.listChildren();

long size = 0;

if (children != null) {
for (Node child : children) {
if (child.hasChildren()) {
size += sumUpAndCacheChildren(child).size;
} else {
size += child.size();
}
}
}

return putInCache(node, size);
}

每个叶节点都有一个大小,而任何祖先节点的大小都被视为其所有后代节点的大小。我想知道每个节点的大小,因此为每个节点聚合并缓存该大小。

这是迭代版本:

private CacheEntry sumUpAndCacheChildren(Node initialNode) {
class StackFrame {
final Node node;
Node[] children;

// Local vars
long size;

// Tracking stack frame state
int stage;
int loopIndex;

StackFrame(Node node) {
this.node = node;
this.children = null;
this.size = 0;
this.stage = 0;
this.loopIndex = 0;
}
}

final Stack<StackFrame> stack = new Stack<StackFrame>();
stack.push(new StackFrame(initialNode));
CacheEntry retValue = getCacheEntry(initialNode);

outer:
while (!stack.isEmpty()) {
final StackFrame frame = stack.peek();
final Node node = frame.node;

switch(frame.stage) {
case 0: {
final CacheEntry entry = getCacheEntry(node);

if (entryIsValid(entry)) {
retValue = entry;
stack.pop();
continue;
}

frame.children = node.asItem().listChildren();
frame.stage = frame.children != null ? 1 : 3;
} break;
case 1: {
for (int i = frame.loopIndex; i < frame.children.length; ++i) {
frame.loopIndex = i;
final Node child = frame.children[i];

if (child.hasChildren()) {
stack.push(new StackFrame(child));
frame.stage = 2; // Accumulate results once all the child stacks have been calculated.
frame.loopIndex++; // Make sure we restart the for loop at the next iteration the next time around.
continue outer;
} else {
frame.size += child.size();
}
}

frame.stage = 3;
} break;
case 2: {
// Accumulate results
frame.size += retValue.size;
frame.stage = 1; // Continue the for loop
} break;
case 3: {
retValue = putInCache(node, frame.type);
stack.pop();
continue;
}
}
}

return retValue;
}

这感觉比需要的更疯狂,并且必须在代码中递归到子级并对它们执行不同操作的所有位置都执行此操作会很痛苦。当我在每个级别聚合并在子级的 for 循环中执行此操作时,我可以使用哪些技术来更轻松地进行递归?

编辑:

在下面的答案的帮助下,我能够大大简化事情。现在的代码几乎与原始递归版本一样简洁。现在,我只需要在其他地方递归相同的数据结构时应用相同的原则。

最佳答案

由于您正在处理树结构并希望计算累积大小,因此在跟踪每个节点的父节点时尝试 DFS。我在这里假设您无法更改或子类 Node 并且我保留了您使用的所有函数签名。

private class SizedNode {
public long cumulativeSize;
public Node node;
public SizedNode parent;

public SizedNode(SizedNode parent, Node node) {
this.node = node;
this.parent = parent;
}

public long getSize() {
if (node.hasChildren()) {
return cumulativeSize;
}
else {
return node.size();
}
}
}

private void sumUpAndCacheChildren(Node start)
{
Stack<SizedNode> nodeStack = new Stack<SizedNode>();

// Let's start with the beginning node.
nodeStack.push(new SizedNode(null, start));

// Loop as long as we've got nodes to process
while (!nodeStack.isEmpty()) {

// Take a look at the top node
SizedNode sizedNode = nodeStack.peek();
CacheEntry entry = getCacheEntry(sizedNode.node);

if (entryIsValid(entry)) {
// It's cached already, so we have computed its size
nodeStack.pop();

// Add the size to the parent, if applicable.
if (sizedNode.parent != null) {
sizedNode.parent.cumulativeSize += sizedNode.getSize();

// If the parent's now the top guy, we're done with it so let's cache it
if (sizedNode.parent == nodeStack.peek()) {
putInCache(sizedNode.parent.node, sizedNode.parent.getSize());
}
}
}
else {
// Not cached.
if (sizedNode.node.hasChildren()) {
// It's got a bunch of children.
// We can't compute the size yet, so just add the kids to the stack.
Node[] children = sizedNode.node.listChildren();
if (children != null) {
for (Node child : children) {
nodeStack.push(new SizedNode(sizedNode, child));
}
}
}
else {
// It's a leaf node. Let's cache it.
putInCache(sizedNode.node, sizedNode.node.size());
}
}
}
}

关于java - 将递归方法(递归在循环内完成)转换为迭代方法,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/28098856/

27 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com