将函数从递归转换为迭代

Posted

技术标签:

【中文标题】将函数从递归转换为迭代【英文标题】:Converting function from recursive to iterative 【发布时间】:2020-11-03 09:34:39 【问题描述】:
-There's a <TL;DR> section down below-

你好,

假设我需要一个算法来处理树中的每个节点,考虑到:

    这棵树很大 处理每个节点非常耗时。

这是基本的**遍历递归函数:

function process_branch(node)
    process_node(node)
    for each c in node.children
        process_branch(c)
    end for
end function

现在,这是迭代版本,使用我自己的堆栈:

function process_branch(node)
    stack.push(node)
    while not stack.empty
        n=stack.pop()
        process_node(n)
        for each c in n.children
            stack.push(c)
        end for
    end while
end function

**我在这里放置的所有算法都是实际算法的极其简化的版本。

到目前为止,很好。

更多考虑:

我正在使用迭代解决方案来避免调用堆栈溢出。在 事实上,使用我自己的堆栈可以让我捕获无内存异常并且 停止该功能。 正如我之前提到的,树的大小很大,没有 提前计算所需内存的方法。 每个节点处理的结果,它们的状态等,都存储在一个数据库中。这样,如果达到内存限制或 功能被其他条件(包括用户意愿)停止, 什么都没有丢失。

但是,我需要一种再次调用函数的方法,恢复处理并忽略已经完成的分支

-<TL;DR> section starts here-

这个递归版本非常棒:

function process_branch(node)
    if not node.completed
        process_node(node)
        for each c in node.children
            process_branch(c)
        end for
        node.completed=true
    end if
end function

注意一个分支的根节点是如何仅在其所有子节点都完成时才标记为完成的。所以,每次重启函数,都省了很多工作。

我的问题是,我无法创建这个新函数的迭代版本。

这是我现在拥有的迭代版本:

function process_branch(node)
    stack.push(node)
    while not stack.empty
        n=stack.pop()
        if not n.completed
            process_node(n)
            for each c in n.children
                stack.push(c)
            end for
            n.completed=true
        end if
    end while
end function

现在请注意,给定节点是如何标记为已完成的甚至在其子分支实际处理之前

通过递归,n.completed=true 语句被延迟到其子项(以及子项的子项等)的每个 process_branch(c) 完成。

<TL;DR> section ends here

所以,如果函数在这个过程中被停止,将会有一些完整的分支被标记为完成(这是错误的),并且在下一次函数重新启动时将被忽略。

如何在迭代版本中实现延迟n.completed=true

我看不到。我希望这只是一个思维障碍。

感谢您的帮助!

EDIT:

感谢关于玩栈的建议,我找到了一个简单的解决方案,基本上需要两件事:

    将父节点(再次)推到其子节点之前,因此之后 每个child处理完,主循环可以返回,设置 已完成。 为每个节点(“已处理”)引入新的状态/属性,用于 区分已处理的节点并跳过已完成的工作。 请注意,这是必需的,因为 处理每个节点 到达后立即发生。或者换句话说,一个节点是 在其分支完成之前处理
function process_branch(node)
    stack.push(node)
    while not stack.empty
        n=stack.pop()
        if not n.completed
            if not n.processed
                process_node(n)
            end if
            t=0
            for each c in n.children
                if not c.completed
                    t=t+1
                end if
            end for
            if t=0
                n.completed=true
            else
                stack.push(n)
            end if
            for each c in n.children
                if not c.completed
                    stack.push(c)
                end if
            end for
        end if
    end while
end function

希望这对将来的某人有所帮助。

【问题讨论】:

在每个节点中存储一个整数childrenLeftToProcess,然后从该值为0且节点未完成的所有节点开始进行迭代算法,处理完一个节点后将其标记为完成并减少父节点childrenLeftToProcess值1,如果等于0,则将其添加到堆栈中 感谢您的评论,@Photon。这显然给了我一个关于更简单解决方案的提示。 【参考方案1】:

您正在使用堆栈来存储“下一步做什么”

不只是存储节点,还可以存储如何处理它们......

function process_branch(node)
    stack.push(new Job(node,true)) // true for processing a node
    while not stack.empty
        j=stack.pop()
        if not j.n.completed
            if (j.process) // test if processing or just marking complete
                process_node(j.n)
            else
                j.n.completed=true
            end if
            for each c in n.children
                stack.push(new Job(c,true)) // true for processing a node
            end for
            stack.push(new Job(n,false)) // false for marking complete
        end if
    end while
end function

您将需要重新安排它以在您中止后重新启动处理...我现在有点着急,所以它只是解决现在节点何时完成的问题,也许已经足够了...

【讨论】:

以上是关于将函数从递归转换为迭代的主要内容,如果未能解决你的问题,请参考以下文章

您如何将其转换为迭代函数而不是使用嵌套循环进行递归?

将分而治之的递归算法转换为迭代版本

将带有全局变量的递归转换为使用堆栈的迭代

将迭代转换为递归

如何在python中将迭代函数转换为递归函数

需要处理嵌套列表时如何将递归转换为迭代?