堆排序

堆排序算法简介:

堆可以看为一颗完全二叉树,满足,任何一个非叶结点的值都不大于(或不小于)其左右孩子结点的值。若父亲大孩子小,则称为大顶堆;若父亲小孩子大,则称为小顶堆
根据堆的定义,其根结点值是最大的(或最小的),因此,将一个无序序列调整为一个堆,就可以找出这个序列的最大(或最小)值,然后将找出的这个值交换到序列的最后(或最前),这样有序的序列元素增加1,无序序列元素减少1,重复操作,就可以实现排序。

举例:

原始序列:49,38,65,97,76,13,27,49(1)
构建大顶堆:原始序列对应的完全二叉树为:
这里写图片描述
结点76,13,27,49(1)是叶子结点,没有左右孩子,所以满足堆的定义。从97开始调整,按97,65,38,49的顺序依次调整非叶子结点。
(1)调整97,因为97>49(1),所以97和它的孩子49(1)满足堆的定义,不需要调整;
(2)调整65,同97;
(3)调整38,38<97, 38<76, 不满足大顶堆的定义,需要调整,因为它的左右孩子都大于它本身,应该和两者最大的那个交换,即和97交换。因为如果和76交换,76<97还是不满足大顶堆的定义。又38和97交换后,38成了49(1)的根结点,且38<49(1),所以继续交换,变为:
这里写图片描述
(4)调整49,原理同调整38的步骤,调整后结果:
这里写图片描述
此时我们已经建立一个大顶堆,对应序列为:97,76,65,49(1),49,13,27,38。然后将堆顶97和序列的最后一个元素38交换。第一趟堆排序完成,97达到最终的位置。将除97外的其他序列,再次调整为大顶堆。现在只需要调整顶点38即可(因为其他的元素没有变化,满足大顶堆的定义)。
调整38后:
这里写图片描述
现有序列:76,49(1),65,38,49,13,27,97。交换堆顶76和序列最后一个元素27,序列变为:27,49(1),65,38,49,13,76,97,则76达到最终的位置。,依次重复上述步骤直到完成排序。

代码:

#include <iostream>
using namespace std;

void Sift(int a[], int low, int high) {
    int left = low * 2 + 1;//左孩子
    int right = left + 1;//右孩子
    int index = low; //记录当前位
    while (left < high && a[left] > a[index]) index = left;
    while (right < high && a[right] > a[index]) index = right;
    if (index != low) {//位置有调整
        int temp = a[index];
        a[index] = a[low];
        a[low] = temp;
        Sift(a, index, high);
    }
}

void heap(int a[], int n) {
    if (n <= 0) return;
    for (int i = n / 2 - 1; i >= 0; --i) {
        //建立最大堆,将堆中最大的值交换到根节点
        Sift(a, i, n);
    }
    for (int i = n - 1; i >= 1; --i) {
        //将当前堆的根节点交换到堆尾的指定位置
        int temp = a[0];
        a[0] = a[i];
        a[i] = temp;
        //建立下一次的最大堆
        Sift(a, 0, i);
    }
}

int main()
{
    int a[8] = { 49,38,65,97,76,13,27,49 };
    int n = 8;
    heap(a, n);
    for (int i = 0; i < n; i++)
    {
        cout << a[i] << " ";
    }
    cout << endl;
    return 0;
}

结果:

这里写图片描述

时间复杂度分析

对于Sift()函数,完全二叉树的高度为log2(n+1)(向上取整),即对于每个节点的调整时间复杂度为O(log2n)。对于heap()函数,基本操作总次数是两个并列的for循环中基本操作次数相加,第一个for循环的基本操作次数是O(log2n)×(n/2),第二个for循环的次数为O(log2n)×(n1),因此整个算法的时间复杂度为O(log2n)×(n/2)+O(log2n)×(n1),化简后的时间复杂度为O(nlog2n)

空间复杂度分析

只需要一个额外的空间temp,因此空间复杂度为O(1)

原文地址:https://www.cnblogs.com/xiaocai-ios/p/7779757.html