当前位置: 首页 > 知识库问答 >
问题:

为什么堆排序的空间复杂度为O(1)?

权胜泫
2023-03-14

我不明白堆排序的空间复杂度是怎样的O(1)?虽然快速排序不使用任何额外的数组(即就地),但在最坏情况下其空间复杂度为O(n),在最佳情况下为O(lg n),因为在递归调用的后端使用堆栈。我说得对吗?

堆排序也是如此。虽然,它是就地的,但是由于Build-Heap函数调用Max-Heapify函数,所以它的空间复杂度应该等于Max-Heapify,即O(lg n)。不是吗?此外,稍后在根节点调用Max-Heapify函数n次,正如我所说的Max-Heapify()空间复杂度为O(lg n)。

所以,堆排序的整体空间复杂度应该是O(lg n)。但是我在维基百科上找到了它O(1)。帮助我理解它。

共有3个答案

龚钧
2023-03-14

heapify有非递归版本(参见下面的示例)。对于快速排序,如果递归仅用于较小的分区,则循环返回以将较大的分区拆分为2(再次在这2个分区中较小的分区上使用递归,依此类推),则最大堆栈空间为O(log(n)),但最坏的情况时间仍然为O(n^2)。

C使用非递归堆排序的非递归堆排序示例:

typedef unsigned int uint32_t;

void HeapSort(uint32_t *, size_t);
void Heapify(uint32_t *, size_t);
void SiftDown(uint32_t *, size_t, size_t);

void HeapSort(uint32_t * a, size_t count)
{
size_t end;
    Heapify(a, count);      // create initial heap
    end = count-1;
    while(end > 0){
        // swap root (largest value) with end
        std::swap(a[0], a[end]);
        // reduce size of heap and
        // increase size of sorted array
        end--;
        // repair the reduced heap
        SiftDown(a, 0, end);
    }
}

// create initial heap: for root = (count-2)/2 -> 0
// parent = root, children = root*2+1, root*2+2
// swap so that all a[parent] > a[child]
void Heapify(uint32_t * a, size_t count)
{
size_t root;
    if(count < 2)
        return;
    // create sub-heaps of increasing size,
    // with root going from (count-2)/2 to 0
    root = (count - 2) / 2;
    while(1){
        SiftDown(a, root, count-1);
        if(root == 0)
            break;
        root--;
    }
}

// scan from root to end, swapping as needed to repair or create heap
void SiftDown(uint32_t * a, size_t root, size_t end){
size_t parent;
size_t child;
    // while at least two children
    for(parent = root; (child = parent * 2 + 2) <= end; ){
        // choose the larger child
        if(a[child-1] > a[child])
            child = child-1;
        // if child > parent then swap, parent = child
        if(a[child] > a[parent]){
            std::swap(a[child], a[parent]);
            parent = child;
        // else done with search for swaps
        } else {
            break;
        }
    }
    // check for final only child
    if((child = parent * 2 + 1) <= end)
        if(a[child] > a[parent])
            std::swap(a[child], a[parent]);
}
黄无尘
2023-03-14

空间复杂性是指算法使用的额外空间。堆排序不使用任何额外空间(在O(n)中),除了数组进行排序。因此它是O(1)

田远
2023-03-14

堆排序不占用任何取决于正在排序的数组大小的空间,只占用数组本身的空间和少数变量。显然是O(1)。

快速排序跟踪需要排序的子数组堆栈。如果您很聪明,并且在任何两个子数组中,将较大的一个子数组放在堆栈上,并立即对较小的一个子数组进行排序,则需要O(log n)。

实际上,这没有任何区别。

 类似资料:
  • 问题内容: 从TimeComplexity文档中可以看出,Python的类型是使用数组实现的。 因此,如果正在使用数组并且进行了一些附加操作,最终您将不得不重新分配空间并将所有信息复制到新空间。 毕竟,这是O(1)最坏的情况吗? 问题答案: 如果查看链接文档中的脚注,您会发现它们包含一个警告: 这些操作依赖于“最坏情况摊销”的“摊销”部分。根据容器的历史记录,各个动作可能会花费惊人的时间。 使用摊

  • 算法相对较新,所以如果我遗漏了一些显而易见的东西,请原谅! 我知道深度优先搜索的空间复杂度通常表示为O(h),其中h是树的高度,而广度优先搜索的复杂度是O(n),其中n是树中节点的数量。 我理解解释(例如在这个答案中https://stackoverflow.com/a/9844323/10083571)也就是说,在BFS中最坏的情况下,所有节点都在一个级别上,这意味着我们必须在遍历它们之前将所有

  • 问题内容: 我打算对StringBuilders中的最后一个字符进行很多删除。使用的解决方案对我来说很好。但是,由于这些删除将处于循环中,因此我需要知道其复杂性。 据我了解,该操作只是减少了StringBuilder对象的一些私有属性,并且不对字符本身执行任何复制/克隆/复制操作,因此它的时间为O(1),应该可以快速运行。 我对吗? 问题答案: 从文档中: 设置字符序列的长度。序列更改为新的字符序

  • 让我们以合并排序的实现为例 a) 这种合并排序的时间复杂度是。并行化(1)和(2)会带来实际收益吗?从理论上讲,在对它们进行并行化之后,似乎最终也会出现。但实际上我们能得到什么好处吗? b) 这种合并排序的空间复杂度是。但是,如果我选择使用链表执行就地合并排序(不确定是否可以合理地使用数组),空间复杂性是否会变得,因为您必须考虑递归堆栈帧大小?既然它不能超过64,我们能把当作常数吗?我可能在几个地

  • 问题内容: 我刚刚开始学习数据结构,并且在进行数组插入时想知道为什么数组插入的时间复杂度为O(n)而不是O(n + 1)? 在最佳情况下,当插入在最后时,时间复杂度为O(1)。我想我们正在考虑1插入元素,因为这里没有元素被移动。在最坏的情况下,假设我们必须移动n个元素然后插入新元素,那么时间时间复杂度是否应该为O(n + 1)?n用于移动元素,1用于插入。 非常感谢您的帮助。 问题答案: O(n)