为什么合并排序空间复杂度On?

问题描述


乍看之下,合并排序具有O(n)的空间复杂性是有道理的,因为要对我正在拆分并创建子数组的未排序数组进行排序,但是所有子数组的大小之和为n。

问题:我主要关心的是递归过程中mergeSort()函数的内存分配。我有一个主堆栈,每个对mergeSort()的函数调用(以回响方式)将被压入堆栈。现在,每个可追溯调用的mergeSort()函数将具有其自己的堆栈。因此,假设如果我们对mergeSort()进行了5次响应调用,则主堆栈将包含5个函数调用,其中每个函数调用将具有其自己的函数堆栈。现在,每个函数堆栈将具有其自己的局部变量,例如该函数创建的左子数组和右子数组。因此,这5个函数堆栈中的每一个都应在内存中具有5个不同的子数组。那么,空间不应该随着回拨电话的增长而增长吗?

enter image description here

解决方法

内存应该是线性的

尽管对mergeSort的每个调用都会触发两个递归调用,所以讨论并绘制递归调用的二叉树是有意义的,但是一次仅执行这两个递归调用中的一个。第一个电话在第二个电话开始之前结束。因此,在任何给定时间,仅探索树的一个分支。 “调用堆栈”代表该分支。

递归树的深度最多为log(n),因此调用堆栈的高度最多为log(n)。

探索一个分支需要多少内存?换句话说,在任何给定时间最多在调用堆栈上分配多少内存?

在调用堆栈的底部,有一个大小为n的数组。

最重要的是大小为n / 2的数组。

最重要的是大小为n / 4的数组。

等等...

因此,调用堆栈的总大小最多为n + n / 2 + n / 4 + ...

因此,调用堆栈的总大小最多为2n。

可能的内存泄漏

如果您的merge sort实现在每个递归调用中分配了一个新数组,而您忘记在调用结束时释放这些数组,那么分配的总内存将成为整棵树所需的总内存,而不仅仅是一个分支。

考虑树中给定深度的所有节点。这些节点的子数组加起来形成整个数组。例如,树的根具有长度为n的数组;然后在其下一级,有两个子数组代表原始数组的两半;然后在其下一级,有四个子数组代表原始数组的四分之四;因此,树的每个级别都需要内存n。树有log(n)个级别。因此,为整个树分配的内存总量为n log(n)。

结论

如果合并排序没有内存泄漏,则其空间复杂度为线性 O(n)。此外,有可能(尽管并不总是很希望)就地实现合并排序,在这种情况下,空间复杂度是恒定的 O(1)(所有操作都直接在输入数组内执行)

但是,如果合并排序的实现存在内存泄漏,即您继续在递归调用中分配新数组,但在递归调用返回时不释放它们,那么它很容易具有空间复杂度 O( n登录n)

相关问答

错误1:Request method ‘DELETE‘ not supported 错误还原:...
错误1:启动docker镜像时报错:Error response from daemon:...
错误1:private field ‘xxx‘ is never assigned 按Alt...
报错如下,通过源不能下载,最后警告pip需升级版本 Requirem...