堆排序 c++

HeapSort
堆排序是基于堆上的一种排序方法,数据存储的结构是数组,该排序的效率接近排序的下界nlog(n)。

学习堆排序前最好理解掌握以下内容

  • 二叉树
  • 二叉堆
  • 最大堆与最小堆概念

这些概念可以自行百度或谷歌,要留意这里”堆”的概念不同于c++里分配内存所说的堆;

堆排序算法原理如下(基于最大堆而言的):

  1. 将长度为n的待排序的数组进行堆有序化构造成一个大顶堆(初始最大堆的构建)
  2. 将根节点与尾节点交换
  3. 将剩余的n -1个节点重新进行堆有序化
  4. 重复步骤2,步骤3直至构造成一个有序序列

总而言之,该算法的思想就是最大堆堆顶的最大数取出,将剩余的堆继续调整为最大堆,再次将堆顶的最大数取出,这个过程持续到剩余数只有一个时结束(有点像选择排序那样每次抽出剩余数据的最大值)

第一步:构造堆(Build_Max_Heap):初始化构造堆所形成的二叉树有点类似于完全二叉树,比如如果树的高度为H,那么前H-1层对应的节点数必须是2^(H-1);第H层必须排满树的左边才能排右边。初始化好后堆节点的访问如下:
在数组起始位置为0的情形中:
1. 父节点i的左子节点在位置 2i+1;
2.父节点i的右子节点在位置 2i+2;
3.子节点i的父节点在位置 (i-1)/2;

// 一开始构建好二叉堆(满足最大堆的特征);
void buildHeap(int *arr, int size)
{
    for (int i = (size - 1) / 2; i >= 0; i--)
    {
        //传入给max_heapify_once 后两个参数为数组的下标;
        max_heapify_once(arr, i, size - 1);
    }
}

2.最大堆调整(Max_Heapify):将堆的末端子节点作调整,使得子节点永远小于父节点

void max_heapify_once(int* arr, int start, int end)
{
    int father = start;
    int son = start * 2 + 1;//左子节点是父子节点的下标*2+1;
    while (son <= end)
    {
        if (son + 1 <= end && arr[son + 1] > arr[son]) //如果右子节点存在并且大于左子节点的情况;
        {
            son++;
        }
        //如果父节点小于两个子节点中最大者就与其交换,这样就会改变该子节点下面的最大堆结构,所以要继续做下去;
        else if (arr[father] < arr[son])  
        {
            swap(arr[father], arr[son]);
            father = son;
            son = father * 2 + 1;
        }
        else //如果父子节点比两个子节点都大直接退出函数,因为子节点下面的子节点已满足最大堆的特征
        {
            return;
        }
    }
}

3.堆排序(HeapSort):移除位在第一个数据的根节点,并做最大堆调整

void heapSort(int* arr, int size)
{
    buildHeap(arr, size);
    for (int i = size - 1; i > 0; i--)
    {
        swap(arr[0], arr[i]);
        max_heapify_once(arr, 0, i-1);
    }
}

总的代码如下:

#include <iostream>
#include <algorithm>
using std::swap;
using std::cout;
using std::endl;
using std::cin;

void max_heapify_once(int* arr, int start, int end)
{
    int father = start;
    int son = start * 2 + 1;//左子节点是父子节点的下标*2+1;
    while (son <= end)
    {
        if (son + 1 <= end && arr[son + 1] > arr[son]) //如果右子节点存在并且大于左子节点的情况;
        {
            son++;
        }
        //如果父节点小于两个子节点中最大者就与其交换,这样就会改变该子节点下面的最大堆结构,所以要继续做下去;
        else if (arr[father] < arr[son])  
        {
            swap(arr[father], arr[son]);
            father = son;
            son = father * 2 + 1;
        }
        else //如果父子节点比两个子节点都大直接退出函数,因为子节点下面的子节点已满足最大堆的特征
        {
            return;
        }
    }
}
// 一开始构建好二叉堆(满足最大堆的特征);
void buildHeap(int *arr, int size)
{
    for (int i = (size - 1) / 2; i >= 0; i--)
    {
        //传入给max_heapify_once 后两个参数为数组的下标;
        max_heapify_once(arr, i, size - 1);
    }
}
void heapSort(int* arr, int size)
{
    buildHeap(arr, size);
    for (int i = size - 1; i > 0; i--)
    {
        swap(arr[0], arr[i]);
        max_heapify_once(arr, 0, i-1);
    }
}

int main()
{
    int n;
    cout << "Please enter the sum of number you want to sort with heap sort: ";
    cin >> n;

    int * arr = new int[n];
    for (int i = 0; i < n; i++)
    {
        cin >> arr[i];
    }
    heapSort(arr, n);
    for (int i = 0; i < n; i++)
    {
        cout << arr[i] << ' ';
    }
    cout << endl;
    delete[] arr;
    system("pause");
    return 0;
}

相关资料推荐:
堆排序维基百科
图解堆排序
堆排序详细讲解

猜你喜欢

转载自blog.csdn.net/ke1950523491/article/details/78738807