快速排序的内存复杂度
Memory complexity of Quicksort
Quicksort
的 space 复杂度是 listed as O(logn)
。
然而 - Quicksort
可以在不使用任何额外内存的情况下进行处理:
在每次迭代中,在分区过程中,
这些条目最终被交换到
基于所使用的枢轴的左右分区。
这种递归交换并因此形成分区
可以在列表本身上完成而无需
在递归调用干扰的一级分区
并且不会在不同级别的呼叫干扰中进行快速排序。
Quicksort
中的额外内存有什么用?
TIA。
//============================
编辑:
同意 comments/answers 中的堆栈 space-- 错过了。
仍然,
Quicksort 在 预期情况下 的执行时间为 O(nlogn)
--
通过在每个递归级别形成(几乎)大小相等的分区。
stack-space使用的是二叉树,最佳情况下是满树,高度log n
。
这棵树上的每个节点都是一个递归调用,stack-space
在这种情况下,顺序为 n
-- 而不是 log n
。这棵树上有 O(n)
个节点。向左和向右的递归调用
分区是同时进行的——调用树在某个执行点已满。
所以-平均情况 space 复杂度应该是 O(n)
-- 而不是 O(logn)
(?)
它也与合并排序的 space 复杂性相矛盾。
合并排序 space 复杂度列为 O(n)
并且处理类似。
递归堆栈space。也就是说,您不需要存储任何额外的 数据 ,但您需要存储 return 地址(编辑:以及适用于相关语言的其他相关信息,谢谢@dmaij 用于提醒)每个递归子调用。由于 Quicksort 生成深度堆栈 log(n)
,您将需要在调用堆栈上同时使用尽可能多的堆栈帧。
Quicksort 通常使用 O(log n) 额外内存,存储在堆栈上。它不是 O(n),因为二叉树在内存中永远不会显式,我们只是对其进行 post 阶遍历(即:只有一个 path 在树曾在给定时间存储过)。
Mergesort 被列为 O(n) 因为我们通常将合并的结果复制到一个新数组中。根据维基百科,就地排序是可能的,但将时间复杂度增加到 O(n log2 n)。它仍然会使用 O(log n) 进行递归。
使用随机主元的快速排序具有 space 复杂度
平均案例 = O(logN)
最坏情况 = O(N)
最坏的情况是排序不平衡。但是,可以通过先对较小的子数组进行排序,然后对较大的数组进行 tail-recursing 排序来避免这种情况。此改进版本的最坏情况 space 复杂度为 O(logN)
。请注意,尾递归是由大多数编译器在内部实现的,不需要为此编写额外的代码。
合并排序的 average/worst 情况 space 复杂度为 O(N)
,因为合并子例程将数组复制到新数组,这需要 O(N)
额外 space.
编辑:
正如评论中所建议的那样,依赖尾递归优化并不好。将它合并到代码中会是一个更好的主意。
Quicksort
的 space 复杂度是 listed as O(logn)
。
然而 - Quicksort
可以在不使用任何额外内存的情况下进行处理:
在每次迭代中,在分区过程中,
这些条目最终被交换到
基于所使用的枢轴的左右分区。
这种递归交换并因此形成分区
可以在列表本身上完成而无需
在递归调用干扰的一级分区
并且不会在不同级别的呼叫干扰中进行快速排序。
Quicksort
中的额外内存有什么用?
TIA。
//============================
编辑:
同意 comments/answers 中的堆栈 space-- 错过了。
仍然,
Quicksort 在 预期情况下 的执行时间为 O(nlogn)
--
通过在每个递归级别形成(几乎)大小相等的分区。
stack-space使用的是二叉树,最佳情况下是满树,高度log n
。
这棵树上的每个节点都是一个递归调用,stack-space
在这种情况下,顺序为 n
-- 而不是 log n
。这棵树上有 O(n)
个节点。向左和向右的递归调用
分区是同时进行的——调用树在某个执行点已满。
所以-平均情况 space 复杂度应该是 O(n)
-- 而不是 O(logn)
(?)
它也与合并排序的 space 复杂性相矛盾。
合并排序 space 复杂度列为 O(n)
并且处理类似。
递归堆栈space。也就是说,您不需要存储任何额外的 数据 ,但您需要存储 return 地址(编辑:以及适用于相关语言的其他相关信息,谢谢@dmaij 用于提醒)每个递归子调用。由于 Quicksort 生成深度堆栈 log(n)
,您将需要在调用堆栈上同时使用尽可能多的堆栈帧。
Quicksort 通常使用 O(log n) 额外内存,存储在堆栈上。它不是 O(n),因为二叉树在内存中永远不会显式,我们只是对其进行 post 阶遍历(即:只有一个 path 在树曾在给定时间存储过)。
Mergesort 被列为 O(n) 因为我们通常将合并的结果复制到一个新数组中。根据维基百科,就地排序是可能的,但将时间复杂度增加到 O(n log2 n)。它仍然会使用 O(log n) 进行递归。
使用随机主元的快速排序具有 space 复杂度
平均案例 = O(logN)
最坏情况 = O(N)
最坏的情况是排序不平衡。但是,可以通过先对较小的子数组进行排序,然后对较大的数组进行 tail-recursing 排序来避免这种情况。此改进版本的最坏情况 space 复杂度为 O(logN)
。请注意,尾递归是由大多数编译器在内部实现的,不需要为此编写额外的代码。
合并排序的 average/worst 情况 space 复杂度为 O(N)
,因为合并子例程将数组复制到新数组,这需要 O(N)
额外 space.
编辑: 正如评论中所建议的那样,依赖尾递归优化并不好。将它合并到代码中会是一个更好的主意。