堆排序

二叉堆的本质是完全二叉树

大顶堆:每个节点的值都大于他的子节点

小顶堆:每个节点的值都小于他的子节点

二叉堆的根节点就是他的堆顶

 

 

堆的存储结构

虽然堆的数据结构是树,但是它并不是以链式结构存储的,而是顺序存储(数组)

通过数组下标可以定位父亲节点或者孩子节点,堆中的最后一个父亲节点就是(数组长度-1)/2,已知父亲节点的下标是n,那么孩子节点的下标就是2n+1和2n+2

 

堆排序的时间复杂度是O(nlogn)

 

堆排序思路

1.从下往上,找父亲节点,先找到堆中的最后一个父亲节点(ArrLen-1)/2,然后对他和他的叶子节点进行排序,方法如下:

  求出两个叶子节点下标,判断两个叶子节点是否在数组范围内,然后挨个比对;

  首先用父节点跟左孩子对比,如果父节点的值小于左孩子,那么最大值的下标就变成左孩子的下标,否则最大值下标就是父节点下标;

  用右节点的值和最大值下标对应的节点进行对比,如果右节点更大,最大下标就变为右节点的下标,否则不变;

  判断:如果最大节点下标不是父节点下标,说明父节点和他的子节点需要重新排序,父节点要和他的左孩子或者右孩子交换顺序,通过最大节点下标将父子节点中的值对换;

  此时,最大值下标就表示换过位的子节点的位置,递归调用,以这个子节点为新的父节点,进行排序,此为一次循环。

2.之前找到的是堆中的最后一给父节点,因此需要向前遍历,将堆中的所有父节点进行排序。所作的操作就是:从(ArrLen-1)/2开始,向前遍历,每次遍历都进行排序操作,直到最终排到堆顶。

 

堆排序代码

#include<iostream>

using namespace std;

void BuildHeap(int ArrLen,int* Array);
void MaxHeapify(int ArrLen,int* Array,int Index);

int main(void)
{
    int* Array;
    int ArrLen;
    cin >> ArrLen;
    Array = new int[ArrLen];
    
    for (int i = 0; i < ArrLen; ++i)
        cin >> Array[i];

    BuildHeap(ArrLen, Array);

    for (int i = 0; i < ArrLen; ++i)
        cout << Array[i] << " ";

    system("pause");
    return 0;
}

void BuildHeap(int ArrLen,int* Array)
{
    // 从最后一个节点的父节点往上调整
    for (int i = (ArrLen-1)/2; i >=0; --i)
        MaxHeapify(ArrLen,Array,i);
}

void MaxHeapify(int ArrLen,int* Array,int Index)
{
    //cout << "Array Index" << Index << endl;

    int Left = Index * 2 + 1;
    int Right = Index * 2 + 2;
    int MaxIndex,Temp;

    if (Left < ArrLen&&Array[Index] < Array[Left])
    {
        MaxIndex = Left;
    }
    else
        MaxIndex = Index;

    if (Right < ArrLen&&Array[MaxIndex] < Array[Right])
        MaxIndex = Right;
    //cout << "Max Index" << MaxIndex << endl;
    if (MaxIndex != Index)
    {
        Temp = Array[Index];
        Array[Index] = Array[MaxIndex];
        Array[MaxIndex] = Temp;
        MaxHeapify(ArrLen, Array, MaxIndex);
    }

}

 

拓展:

求数组中第K大的数

1.使用堆排序来做就是在一次堆排序中,将最大值排到堆顶,然后将堆顶与堆底调换位置(数组的第一个元素和最后一个元素交换位置)

2.再进行堆排序,不理最后一个数(就是冒出来的堆顶),堆排序的长度缩减为原来的ArrLen-1(这样最后一个元素就不参与堆排序了)

3.循环执行k次,即可得到第k大的数

#include<iostream>

using namespace std;

void BuildHeap(int ArrLen,int* Array);
void MaxHeapify(int ArrLen,int* Array,int Index);
int KLargest(int ArrLen, int* Array, int K);

int main(void)
{
    int* Array;
    int ArrLen;
    cin >> ArrLen;
    Array = new int[ArrLen];
    
    for (int i = 0; i < ArrLen; ++i)
        cin >> Array[i];

    BuildHeap(ArrLen, Array);

    /*for (int i = 0; i < ArrLen; ++i)
        cout << Array[i] << " ";*/

    int K;
    cin >> K;
    cout << KLargest(ArrLen, Array, K);

    system("pause");
    return 0;
}

int KLargest(int ArrLen, int* Array, int K)
{
    int PopValue;
    for (int i = 0; i < K; ++i)
    {
        BuildHeap(ArrLen + 1 - K, Array);
        for (int j = 0; j < ArrLen; ++j)
            cout << Array[j] << " ";
        cout << endl;

        PopValue = Array[0];
        int Temp;
        Temp = Array[ArrLen - i-1];
        Array[ArrLen - i-1] = Array[0];
        Array[0] = Temp;

        for (int j = 0; j < ArrLen; ++j)
            cout << Array[j] << " ";
        cout << endl;

    }
    return PopValue;
}


void BuildHeap(int ArrLen,int* Array)
{
    // 从最后一个节点的父节点往上调整
    for (int i = (ArrLen-1)/2; i >=0; --i)
        MaxHeapify(ArrLen,Array,i);
}

void MaxHeapify(int ArrLen,int* Array,int Index)
{
    //cout << "Array Index" << Index << endl;

    int Left = Index * 2 + 1;
    int Right = Index * 2 + 2;
    int MaxIndex,Temp;

    if (Left < ArrLen&&Array[Index] < Array[Left])
    {
        MaxIndex = Left;
    }
    else
        MaxIndex = Index;

    if (Right < ArrLen&&Array[MaxIndex] < Array[Right])
        MaxIndex = Right;
    //cout << "Max Index" << MaxIndex << endl;
    if (MaxIndex != Index)
    {
        Temp = Array[Index];
        Array[Index] = Array[MaxIndex];
        Array[MaxIndex] = Temp;
        MaxHeapify(ArrLen, Array, MaxIndex);
    }

}

 

posted @ 2020-10-08 23:05  Wangtn  阅读(261)  评论(0编辑  收藏  举报