【问题标题】:How to transfer an outside recursion program into a non-recursive form (using stack not CPS)?如何将外部递归程序转换为非递归形式(使用堆栈而不是 CPS)?
【发布时间】:2018-01-20 10:27:37
【问题描述】:

关于如何将递归转换为非递归有很多问题,我也可以将一些递归程序转换为非递归形式 注意:我用的是泛化的方式(用户自定义的Stack),因为我觉得比较容易理解,而且我用的是Java,所以不能用GOTO关键字。

事情并不总是那么顺利,当我遇到回溯时,我被卡住了。例如,子集问题。我的代码在这里:recursive call with loop

当我使用用户定义的堆栈将其转换为非递归形式时。我不知道如何处理循环(在循环中存在递归调用)。

我google了一下发现有很多方法比如CPS。我知道有一个子集问题的迭代模板。但我想使用用户定义的堆栈来解决。

有人可以提供一些线索来将这种递归(循环递归)转换为非递归形式(通过使用用户定义的堆栈,而不是 CPS 等)吗?

这是我的代码recursive to non-recusive(Inorder-Traversal),因为没有递归调用的循环,所以我可以轻松做到。同样,当具有返回值的递归程序时,我可以使用引用并将其作为参数传递给函数。从代码中,我使用堆栈模拟递归调用,并使用“状态”变量到下一个调用点(因为java不允许使用GOTO)。

以下是我收集到的信息。似乎它们都不能满足我提到的问题(有些使用 goto 不允许 java,有些是非常简单的递归意味着没有嵌套递归调用或带循环的递归调用)。

1 Old Dominion University

2codeproject

----------------------分割线------------ --------------------------

谢谢大家。在我发布问题之后......我花了一整夜才弄清楚。这是我的解决方案:non-recursive subset problem solution,代码的注释是我的想法。

总结一下。我之前坚持的是如何处理 foo-loop,实际上,我们可以简单地忽略它。因为我们使用的是loop+stack,所以可以对是否满足条件做一个简单的判断。

【问题讨论】:

    标签: java algorithm recursion recursive-backtracking non-recursive


    【解决方案1】:

    在您的堆栈中,您是否考虑过推送 i(迭代变量)? 通过这样做,当你弹出这个值时,你知道你在被压入堆栈之前是在哪个循环迭代中,因此你可以迭代到下一个 i 并继续你的算法。

    【讨论】:

    • 是的,我将所有本地 var & param & return 地址推送到堆栈。但是昨天我不知道如何将 for 循环与递归调用分开,现在我知道了。我将我的解决方案粘贴到问题中。
    【解决方案2】:

    非负数仅为简单起见。 (也没有 IntFunction。)

    这里定义的power 函数是一个非常简单的例子。

    int power(int x, int exponent) {
        if (exponent == 0) {
            return 1;
        } else if (exponent % 2 == 0) {
            int y = power(x, exponent /2);
            return y * y;
        } else {
            return x * power(x, exponent - 1);
        }
    }
    

    现在堆栈可以按照与部分结果相反的顺序执行,即您在递归中对结果所做的操作。

    int power(final int x, int exponent) {
        Stack<Function<Integer, Integer>> opStack = new Stack<>();
        final Function<Integer, Integer> square = n -> n * n;
        final Function<Integer, Integer> multiply = n -> x * n;
        while (exponent > 0) {
            if (exponent % 2 == 0) {
                exponent /= 2;
                opStack.push(square);
            } else {
                --exponent;
                opStack.push(multiply);
            }
        }
        int result = 1;
        while (!opStack.isEmpty()) {
            result = opStack.pop().apply(result);
        }
        return result;
    }
    

    另一种方法是用布尔值“编码” if-else 的两个分支(奇数/偶数指数):

    int power(final int x, int exponent) {
        BooleanStack stack = new BooleanStack<>();
        while (exponent > 0) {
            boolean even = exponent % 2 == 0;
            stack.push(even);
            if (even) {
                exponent /= 2;
            } else {
                --exponent;
            }
        }
        int result = 1;
        while (!stack.isEmpty()) {
            result *= stack.pop() ? result : x;
        }
        return result;
    }
    

    所以必须区分:

    • 如何准备递归参数
    • 如何处理递归调用的部分结果
    • 如何合并/处理函数中的多个递归调用
    • 利用好东西,比如 x 是一个最终常量

    不难,也许令人费解,所以玩得开心。

    【讨论】:

    • 从我的观点来看,幂函数不同于“循环递归”。幂函数我可以很容易地变成非递归的。并且不要使用 lambda 表达式。 1.给函数加上一个param(代表一个返回值),那么幂就变成了一个void函数。 2.分成不同的阶段,3.push/pop 没有大脑操作hhh......无论如何谢谢你的回答。
    猜你喜欢
    • 1970-01-01
    • 2011-03-24
    • 1970-01-01
    • 2018-12-02
    • 2015-10-26
    • 2015-06-06
    • 2017-03-12
    • 2012-11-13
    • 2014-04-12
    相关资源
    最近更新 更多