堆排序

参考:

https://www.bilibili.com/video/BV1Eb41147dK?from=search&seid=7630499509795698118

https://baike.baidu.com/item/%E5%A0%86%E6%8E%92%E5%BA%8F/2840151?fr=aladdin

https://baike.baidu.com/item/%E5%A0%86/20606834?fr=aladdin

 

 

一,堆排序概述

1,堆排序(Heapsort)

  堆排序是利用堆这种数据结构所设计的一种排序算法。

2,堆(heap)

  ① 堆通常是一个可以被看做一棵树的数组对象。

  ② 堆满足以下两点性质:

    Ⅰ 堆是一颗完全二叉树,其添加新节点的顺序是:从上到下,从左到右

    Ⅱ 子节点<= 父节点 或 子节点 >= 父节点

  ③ 堆可以根据节点大小分为大根堆和小根堆,其中,根结点最大的堆为大根堆,根结点最小的堆为小根堆

3,堆影响排序结果

    大根堆的排序结果是升序序列

    小根堆的排序结果是降序序列

4,算法思想(升序)

  堆排序的本质与选择排序一样,不过堆排序是利用维护堆来进行选择,从而将选择的时间复杂度降下来。

  具体来讲,堆排序是将有序区设在数组的末尾,然后把无序区的元素构造成大根堆,然后从堆(无序区中)选择一个最大值,交换到无区序的第一个元素 / 最后一个元素p,作为新选择出来的元素添加到有序区。一直循环到无序区为空。

 

二,heapify 函数(堆调整 / 筛选)

  1,前提说明:

    由于堆是用数组(数组下标从 0 开始)表示的二叉树,所以对于结点 i ,我们就可知道其父节点与两个子节点的下标(如果有的话)

      父节点 P:(i - 1) / 2

      左子节点 C1:2 * i + 1

      右子节点 C2:2 * i + 2

  2,函数功能:

    (默认子节点以下的所有最小单位的完全二叉树都已经构造成堆)

    将结点 i 及其两个子节点组成的一颗最小单位的完全二叉树,构造成堆,同时递归维护 C1 和 C2 的子孙节点所组成的所有最小单位的堆。

    又因为是尾递归,所以也可以用循环替换。

  3,注意事项

    ① C1 和 C2 要判断有无超出边界,即 C1 和 C2 的下标是否小于 n

    ② 函数的递归出口为:子节点 i 的下标大于等于 n

    因为,递归是往从父节点往叶节点遍历,所以只可能是下溢出

 

三,buildHeap 函数(构建堆)

  1,函数功能

    将一棵完整的完全二叉树构造成堆

  2,函数思路

    由于 heapify 会递归调用维护下面的堆,所以要构造完整的堆,只需要从下面往上构造:

      即从最后一个父结点开始向上调用 heapify

   3,最后一个父节点的下标

      因为最后一个节点的下标为:n-1,

      所以最后一个父节点的下标就是最后一个节点的父节点的下标,即 ((n-1) - 1 ) / 2

 

四,heapSort 函数(堆排序)

  1,函数功能

    利用堆进行堆排序

  2,算法思想

    上面有讲。

  3,算法步骤

    ① 将根节点与堆的最后一个节点交换,每次交换后,将最后一个节点踢出堆的区间范围。

    ② 用 heapify 函数维护堆

      没有必要用 buildHeap函数,因为此时只有根节点所在的最小单位的堆出了问题,其它的最小单位的堆都是正常的。

 

五,堆排序代码

递归

#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#define N 110
int a[N];
void swap(int i, int j)  // 交换结点位置
{
    int t = a[i];
    a[i] = a[j];
    a[j] = t;
}
void heapify(int n, int i)  // 堆调整
{
    if (2 * i + 1 >= n)
        return;
    int c = 2 * i + 1;
    if (c + 1 < n&&a[c] < a[c + 1]) // 求两个子节点中的较大值
        c++;
    if (a[c] > a[i])
    {
        swap(c, i);    // 将最大值移至父节点
        heapify(n, c); // 递归维护下面的结点
    }
}
void buildHeap(int n)  // 构造堆
{
    int last = n - 1;
    int p = (last - 1) >> 1;  // 最后一个父结点
    for (int i = p; i >= 0; i--)  // 从最后一个父结点开始向上调用 heapify
        heapify(n, i);
}
void heapSort(int n)  // 利用堆进行堆排序
{
    buildHeap(n);
    for (int i = n - 1; i > 0; i--)
    {
        swap(i, 0);     // 交换根节点与堆的最后一个节点
        heapify(i, 0);  // 维护根节点所在的最小单位的堆
    }
}
int main(void)
{
    int n;
    while (scanf("%d", &n) != EOF)
    {
        for (int i = 0; i < n; i++)
            scanf("%d", &a[i]);

        heapSort(n);
        for (int i = 0; i < n; i++)
            printf("%d ", a[i]);
        puts("");
    }
}
View Code

非递归

#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#define N 110
int a[N];
void swap(int i, int j)  // 交换结点位置
{
    int t = a[i];
    a[i] = a[j];
    a[j] = t;
}
void heapify(int n, int i)  // 堆调整
{
    while (2 * i + 1 < n)
    {
        int c = 2 * i + 1;
        if (c + 1 < n&&a[c] < a[c + 1]) // 求两个子节点中的较大值
            c++;
        if (a[c] > a[i])
        {
            swap(c, i);    // 将最大值移至父节点
            i = c;
        }
        else
            break;
    }
}
void buildHeap(int n)  // 构造堆
{
    int last = n - 1;
    int p = (last - 1) >> 1;  // 最后一个父结点
    for (int i = p; i >= 0; i--)  // 从最后一个父结点开始向上调用 heapify
        heapify(n, i);
}
void heapSort(int n)  // 利用堆进行堆排序
{
    buildHeap(n);
    for (int i = n - 1; i > 0; i--)
    {
        swap(i, 0);     // 交换根节点与堆的最后一个节点
        heapify(i, 0);  // 维护根节点所在的最小单位的堆
    }
}
int main(void)
{
    int n;
    while (scanf("%d", &n) != EOF)
    {
        for (int i = 0; i < n; i++)
            scanf("%d", &a[i]);

        heapSort(n);
        for (int i = 0; i < n; i++)
            printf("%d ", a[i]);
        puts("");
    }
}
View Code

 

 

六,堆排序算法分析

  空间复杂度

    仅用常数个辅助单元,所以空间复杂度为 O(1)

  时间复杂度

    建堆时间为 O(n),之后有 n-1 次堆调整,调整的时间与树高有关,为 O(h),所以,时间复杂度为 O(nlog2n)

  算法的稳定性

    因为在 heapSort 函数中,可能把原本在前面的值相同的元素交换到后面去,所以堆排序算法是一种不稳定的排序算法。

 

 

 

========== ========= ========= ======= ====== ===== ==== === == =

  寄扬州韩绰判官  杜牧 唐

青山隐隐水迢迢,秋尽江南草未凋。

二十四桥明月夜,玉人何处教吹箫。

 

 

posted @ 2020-03-04 17:14  叫我妖道  阅读(701)  评论(2编辑  收藏  举报
~~加载中~~