【问题标题】:Why don't we consider stack frame sizes while calculation Space Complexity of recursive procedures?为什么在计算递归过程的空间复杂度时不考虑堆栈帧大小?
【发布时间】:2014-01-12 15:32:55
【问题描述】:

考虑Merge Sort 在包含n 元素的int Array 上的情况,我们需要一个额外的大小为n 的数组来执行合并。不过我们最终丢弃了额外的数组。所以归并排序的空间复杂度为O(n)。 但是,如果您查看递归 mergeSort 过程,则在每次递归调用 mergeSort(something) 时都会将一个堆栈帧添加到堆栈中。它确实需要一些空间,对吗?

public static void mergeSort(int[] a,int low,int high)
{
    if(low<high)
    {
        int mid=(low+high)/2;
        mergeSort(a,low,mid);
        mergeSort(a,mid+1,high);
        merge(a,mid,low,high);
    }
}

我的问题是:

  1. 为什么我们不考虑堆栈帧的大小,而 计算合并排序复杂度?
  2. 是不是因为栈只包含了几个整数变量和 一个参考,不会占用太多内存?
  3. 如果我的递归函数创建了一个新的本地数组怎么办(比如说int a[]=new int [n];)。那么在计算空间复杂度时会考虑它吗?

【问题讨论】:

  • 2.是1的正确答案。至于3,
  • 请注意,数组不会在 Java 中的堆栈上。只有对数组的引用。
  • 真的!在 JAVA 中只会有一个关于堆栈的引用。在空间复杂度中是否应该考虑该参考的大小?
  • 参考大小为O(1);对于归并排序,堆栈帧数 = 递归深度是对数的 O(log n)。仅当您设法使merge 在空间中成为 O(1)(即就地合并)时,这才重要。那么是的,而不是 O(1) 算法,你将有 O(log n) 算法,空间方面。
  • @WillNess 你是对的,但正如答案中所指定的,所有堆栈帧不会同时存在于堆栈中。它们在程序执行过程中不断减少和增加。

标签: java arrays algorithm recursion space-complexity


【解决方案1】:

绝对应该考虑堆栈消耗的空间,但这里有些人可能不同意(我相信有些算法甚至会提出复杂性声明而忽略了这一点 - 有一个关于基数排序的未回答的相关问题在某处浮动)。

由于我们在每次递归调用时将数组一分为二,因此堆栈的大小将为O(log n)

因此,如果我们将其考虑在内,总空间将为O(n + log n),即O(n)(因为,在大O 表示法中,我们可以丢弃渐近更小的项),所以它不会改变复杂性。

对于创建本地数组,类似的参数也适用。如果你在每一步都创建一个本地数组,你最终会得到O(n + n/2 + n/4 + n/8 + ...) = O(2n) = O(n)(因为,在大O表示法中,我们可以丢弃常数因子),所以这也不会改变复杂性。

【讨论】:

  • 会有nmergeSort()的递归调用,每个栈帧的大小都是一样的,不是吗?
  • @AmanArora 每个堆栈帧的大小将是相同的,并且会有O(n) 递归调用,但它们不会同时都在堆栈上 - 想象一棵二叉树,其中root 的大小为n,每个子节点的大小是父节点的一半 - 虽然整个树可能有 O(n) 节点,但如果你遍历这棵树,你只会深入到树的最大深度,即O(log n) - 一旦你从任何节点返回,它就不再在堆栈上。
  • 所以我的结论是“我们应该考虑堆栈帧大小?”
  • @AmanArora 那太完美了。但是,如果为了上课的目的,你也许应该问问你的讲师他/她的想法,然后照着做,以防止将来可能出现分歧/失分。
【解决方案2】:

因为您在执行此操作时并未计算空间复杂度。这就是所谓的确定:您正在做测试,并试图通过查看结果来得出空间复杂度的结论。这不是数学方法。

是的,您对陈述 2 的看法是正确的。

【讨论】:

  • 对!但是为什么我们不在这些测试中考虑堆栈的大小呢?
  • 如果你想确定空间复杂度而不是计算它,你可以考虑堆栈大小。
猜你喜欢
  • 1970-01-01
  • 2016-04-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-03-14
  • 2021-04-03
  • 2017-07-07
相关资源
最近更新 更多