【问题标题】:Why space complexity of heap sort is O(1)?为什么堆排序的空间复杂度是O(1)?
【发布时间】:2016-09-02 19:00:43
【问题描述】:

我不明白堆排序的空间复杂度是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)。帮助我理解它。

【问题讨论】:

  • Quicksort 使用 O (log n) 空间,除非实现很愚蠢。
  • 维基不写O(1),而是O(1) auxiliary
  • 堆排序可以使用除了原始存储之外的固定数量的额外空间来完成——堆本身可以通过交换值来创建。因此,需要 额外的空间是 O(1)。
  • @programmer: heapify 是迭代的;递归版本是一个简单的尾调用。快速排序并非如此;要消除递归,需要一个堆栈。

标签: algorithm sorting time-complexity heapsort


【解决方案1】:

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

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

在实践中它没有任何区别。

【讨论】:

    【解决方案2】:

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

    【讨论】:

    • 那么快速排序的空间复杂度在最佳情况下为 O(log n),在最坏情况下为 O(n)。请帮我理解。据我所知,快速排序也不会占用任何额外空间。
    【解决方案3】:

    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]);
    }
    

    【讨论】:

      猜你喜欢
      • 2017-09-01
      • 2019-11-09
      • 1970-01-01
      • 2020-12-14
      • 2017-11-12
      • 2020-05-26
      • 2021-06-18
      • 2020-09-11
      • 2014-08-02
      相关资源
      最近更新 更多