当前位置:   article > 正文

堆排序超详细讲解C语言_堆排序c语言

堆排序c语言


堆排序是指利用堆这种数据结构所设计的一种排序算法。堆积是一个近似完全二叉树的结构,并同时满足堆积的性质:即子结点的键值或索引总是小于(或者大于)它的父节点。堆排序可以说是一种利用堆的概念来排序的选择排序。

算法步骤

先将数组变成堆,然后将堆顶元素与数组尾元素交换,然后数组向左收缩,重新建堆……依此类推。

动图演示

img

静图演示

image-20220828171347007

代码实现

堆的向上调整法

void AdJustUp(HeapDateType* array, int child){
    int parent = (child - 1) / 2;
    while(child > 0){
        if(array[child] < array[parent]){
            Swap(&array[child], &array[parent]);
            child = parent;
            parent = (child - 1) / 2;
        }
        else{
            break;
        }
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

堆的向下调整法

void AdJustDown(HeapDateType* array, int arraySize, int parent){
    int child = parent * 2 + 1;
    while(child < arraySize){
        if(child + 1 < arraySize && array[child] > array[child + 1]){
            child++;
        }
        if(array[child] < array[parent]){
            Swap(&array[child], &array[parent]);
            parent = child;
            child = parent * 2 + 1;
        }
        else{
            break;
        }
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

想要了解数据结构堆以及向上、向下调整法,请移步顺序二叉树(堆)与链式二叉树的C语言实现

方式一

需要用到数据结构——堆,(升序)建小堆,然后将数组元素全部插入进堆中,再依次取堆顶元素放入数组中。

但这种方式极其不好:

建堆的时间复杂度为 O ( N ∗ l o g N ) O(N*log^{N}) O(NlogN)空间复杂度为 O ( N ) O(N) O(N)

排序的时间复杂度为 O ( N ∗ l o g N ) O(N*log^{N}) O(NlogN)空间复杂度为 O ( 1 ) O(1) O(1)

若待排数据量庞大,则有可能不能一次性加载到内存中

void HeapSort(int* a, int n){
    Heap obj;
    HeapInit(&obj);
    for(int i = 0; i < n; i++){
        HeapPush(&obj, a[i]);
    }
    for(int i = 0; i < n; i++){
        a[i] = HeapTop(&obj);
        HeapPop(&obj);
    }
    HeapDestroy(&obj);
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

方式二

不利用数据结构——堆,而是直接对数组进行建堆,利用堆的向调整法

建堆的时间复杂度为 O ( N ∗ l o g N ) O(N*log^{N}) O(NlogN)空间复杂度为 O ( 1 ) O(1) O(1)

排序的时间复杂度为 O ( N ∗ l o g N ) O(N*log^{N}) O(NlogN)空间复杂度为 O ( 1 ) O(1) O(1)

但也不推荐,因为有更快的方式三

void HeapSort(int* a, int n){
    for(int i = 0; i < n; i++){
        AdJustUp(a, i);
    }
    for(int i = n - 1; i > 0; i--){
        Swap(&a[0], &a[i]);
        AdJustDown(a, i, 0);
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

方式三

直接对数组进行建堆,利用堆的向调整法

建堆的时间复杂度为 O ( N ) O(N) O(N)空间复杂度为 O ( 1 ) O(1) O(1)

排序的时间复杂度为 O ( N ∗ l o g N ) O(N*log^{N}) O(NlogN)空间复杂度为 O ( 1 ) O(1) O(1)

void HeapSort(int* a, int n){
    for(int i = (n - 1 - 1) / 2; i >= 0; i--){
        AdJustDown(a, n, i);
    }
    for(int i = n - 1; i > 0; i--){
        Swap(&a[0], &a[i]);
        AdJustDown(a, i, 0);
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

复杂度、稳定性分析

堆排序一般只用方式三,这里仅对方式三进行分析

  1. 时间复杂度

    • 建堆:

      image-20220828175101135

      第一层, 2 0 2^{0} 20个节点,需要向下移动 h − 1 h-1 h1层;

      第二层, 2 1 2^{1} 21个节点,需要向下移动 h − 2 h-2 h2层;

      第三层, 2 2 2^{2} 22个节点,需要向下移动 h − 3 h-3 h3层;

      第四层, 2 3 2^{3} 23个节点,需要向下移动 h − 4 h-4 h4层;

      ……

      h − 1 h-1 h1层, 2 h − 2 2^{h-2} 2h2个节点,需要向下移动 1 1 1层;

      则需要移动节点总的移动步数为:

      T ( n ) = 2 0 ∗ ( h − 1 ) + 2 1 ∗ ( h − 2 ) + 2 2 ∗ ( h − 3 ) + . . . 2 h − 3 ∗ 2 + 2 h − 2 ∗ 1 T(n) = 2^{0}*(h-1)+2^{1}*(h-2)+2^{2}*(h-3)+...2^{h-3}*2+2^{h-2}*1 T(n)=20(h1)+21(h2)+22(h3)+...2h32+2h21

      计算得:

      T ( n ) = n − l o g 2 n + 1 ≈ n T(n) = n - log_2^{n+1}\approx n T(n)=nlog2n+1n

      因此建堆的时间复杂度为 O ( N ) O(N) O(N)

    • 排序:

      每次将堆顶元素放入数组尾时都会进行一次堆的向下调整,而由于堆的向下调整时间复杂度为 O ( l o g N ) O(log^{N}) O(logN)

      排序的时间复杂度为 O ( N ∗ l o g N ) O(N*log^{N}) O(NlogN)

  2. 空间复杂度

    仅仅使用了常数个辅助单元,空间复杂度是O(1);

  3. 稳定性

    在一个长为n 的序列,堆排序的过程是从第n/2开始和其子节点共3个值选择最大(大顶堆)或者最小(小顶堆),这3个元素之间的选择当然不会破坏稳定性。但当为n /2-1, n/2-2, …1这些个父节点选择元素时,就会破坏稳定性。有可能第n/2个父节点交换把后面一个元素交换过去了,而第n/2-1个父节点把后面一个相同的元素没 有交换,那么这2个相同的元素之间的稳定性就被破坏了;

    因此堆排序是不稳定的

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/我家自动化/article/detail/615343
推荐阅读
相关标签
  

闽ICP备14008679号