递归与堆栈实现。为什么当Stack没有时递归会返回StackOverflow?

时间:2014-03-16 22:12:34

标签: java performance recursion stack

此问题的基础:

今年夏天我将获得CS学位,而不是曾经有一位教授强调Stack的重要性。但是,我有多个项目都专注于递归的使用。我发现递归很有用且令人兴奋,我在个人项目中经常使用它。

我最近去过面试,面试官对他们问题的递归解决方案感到非常失望。他们想要Stack解决方案。我做了很多研究,但我还不确定何时使用它。

鉴于以下演示:

public class TestCode {

    static long startTime = 0;
    static long stopTime = 0;
    static long totalTime = 0;

    public static void main(String[] args) throws IOException {
        int x = 10000;
        startTime = System.currentTimeMillis();
        recursiveMethod(x);
        System.out.println();
        stopTime = System.currentTimeMillis();
        totalTime = stopTime - startTime;
        System.out.println(totalTime);
        System.out.println();

        startTime = System.currentTimeMillis();
        stackMethod(x);
        System.out.println();
        stopTime = System.currentTimeMillis();
        totalTime = stopTime - startTime;
        System.out.println(totalTime);
        }

    public static void recursiveMethod(int a){
        if(a >= 0){
            recursiveMethod(a - 1);
            System.out.println("Recursion: " + a + " ");
        }
    }

    public static void stackMethod(int a){
        Stack<Integer> myStack = new Stack<Integer>();
        while(a >= 0){
            myStack.push(a);
            a--;
        }
        while(myStack.isEmpty() == false){
            a = myStack.pop();
            System.out.println("Stack: " + a + " ");
        }
    }
}

两种解决方案都在大约200毫秒内完成。通过添加零来更改x的值:x = 100000会给我一个StackOverflowError(在递归方法上)。

当我注释掉SAME值为x的递归解决方案时,程序运行成功,这意味着Stack解决方案远远超出了递归解决方案的限制。

问题

  • 为什么递归解决方案产生的StackOverflowError与Stack解决方案的迭代次数相同,但Stack解决方案不会出错?
  • 如果Stack解决方案功能更强,内存更少,您何时会使用递归解决方案?
  • 在选择一个之前必须考虑的递归和堆栈/迭代解决方案之间的根本区别是什么?

5 个答案:

答案 0 :(得分:10)

  

为什么递归解决方案产生的StackOverflowError与Stack解决方案的迭代次数相同,但Stack解决方案不会出错?

递归使用线程堆栈,并且具有更低的限制。 STack使用堆,这个更大,但最终会出现OutOfMemoryError。

  

如果Stack解决方案更强大并且使用更少内存,您何时会使用递归解决方案?

速度慢,写入更难,更容易出错并且占用更多内存。只是限制要高得多。

  

我看到很多人在Stacks上使用递归,这是因为递归更容易&#34;虽然效率低得多? (3行代码与本例中的7行)。

你还没有预热代码,说哪个更有效率。我会忽略前10,000次迭代或第一次运行时间。

  

虽然我知道你不能说为什么其他人都使用递归,

通常递归的效率低于使用迭代的效率,但是使用递归时不能轻易地重构某些问题。使用Java中的迭代,95%的问题更有效。

  

我想对它的使用提出质疑,而不是使用它,因为&#34;其他人都会这样做。&#34;如果没有充分理由的话。

大多数时候人们使用递归是因为它更简单,更快,但它可能会耗尽堆栈空间,在极少数情况下你必须使用Stack或ArrayList,它比Stack更有效。

答案 1 :(得分:2)

应该记住一件事:从数学和信息技术的角度来看,递归和迭代是等价的。

答案 2 :(得分:1)

大多数机器架构使用固定大小的堆栈,因此递归算法的内存量有限。但是,您分配的显式堆栈可以增长到主内存的大小;因此它有更多的空间可供使用。将它与递归堆栈每帧必须包含更多数据然后显式堆栈的事实相结合,您可以看到为什么堆栈实现更有效。

递归算法通常看起来更优雅;但基于堆栈的算法通常是出于这些原因的更好选择。只有当递归级别的数量紧密限制时才应考虑递归算法。

答案 3 :(得分:1)

  

为什么递归解决方案产生的StackOverflowError与Stack解决方案的迭代次数相同,但Stack解决方案不会出错?

因为递归比使用堆栈使用更多内存?它说哪些都应该在相同的迭代次数下失败?

  

如果Stack解决方案更强大并且使用更少内存,您何时会使用递归解决方案?

首先,向我展示一下'Stack解决方案更强大'的情况。这不是一个有意义的问题。你的第三个也不是。许多算法的递归版本更容易(a)编写和(b)理解。例如,考虑分流码算法与递归下降表达式解析器。

答案 4 :(得分:1)

递归为许多问题提供了易于阅读的简单解决方案,但它存在与其他人指出的功能堆栈大小限制相关的问题。

我个人的理论是,如果递归深度为O(log n),则可以使用递归。

为了对事物进行透视,对平衡二叉树的遍历是+/- O(log n)递归深度(虽然这可以通过堆栈轻松完成,但我在这里使用它)

但是如果你的递归深度不能在O(log n)中受到约束,那么很有可能会耗尽函数堆栈空间。如果您尝试将程序编写到flood fill,则无法使用递归。