数据结构| |堆排序

版权声明:本文为博主原创文章,遵循 CC 4.0 by-sa 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/qq_40399012/article/details/81952699

堆排序:也是选择排序的一种,只不过是利用树形结构来进行排序
优点:时间复杂度为O(nlogn)

分为两种:
一:从小到大(非递减排序)
二:从大到小(非递增排序)

对于从小到大采用大顶堆来进行排序。

每次取大顶堆的堆顶,将堆顶与最后一个元素进行交换,然后在对堆进行排序调整使其变为大顶堆,然后再取堆顶元素,让堆顶元素再和倒数第二个元素进行交换,一直这样循环,知道只剩下一个元素没有进行交换,那么该组元素,就变成了一个有序的序列。

未初始化的堆

现将其变成大顶堆,如图:
大顶堆
然后再将堆顶元素,与最后一个元素进行交换,对这个堆再进行调整,不再管最后一个元素,然后再在交换在调整,直到只剩下一个堆顶元素的时候。也就排序完成了。
如图:
交换元素
然后不再管78这个位置了,这已经是最大值了。
将前面的元素在进行一个调整为大顶堆即可。
如图:
调整一次
再交换:再调整:
如图:
交换第二次
调整第二次
再交换,再调整
如图:
交换第三次
调整第三次
再交换,再调整
如图:
交换第四次
调整第四次
再交换,不再调整
如图:
交换第五次

因为交换完了之后,就只剩下堆顶一个元素了,没有元素再可供调整了。所以结束。此时数据元素变为了非递减的顺序。

接下来看代码该如何实现:

//向下调整堆(调为大堆)
void ADJustDown(HPDataType* a, int size, int parent)
{
    assert(a);

    //左孩子结点
    int child = parent * 2 + 1;
    //左孩子结点的下标小于数组的最大下标时,堆是一个完全二叉树
    while (child < size)
    {
        //取出最大的孩子结点,只有当右孩子存在时,才能求两个中较大的一个孩子
        if (a[child + 1] > a[child] && ((child + 1) < size))
        {
            child++;
        }

        //交换结点,向下调整
        if (a[parent] < a[child])
        {
            Swap(&a[parent], &a[child]);
            parent = child;
            child = parent * 2 + 1;
        }

        //如果要调整的的这个结点,比起的孩子结点大的话,那就不用调整了
        else
        {
            break;
        }
    }
}
//思想:开始将其变为一个大顶堆,然后每次将堆顶与最后一个元素进行交换,在令该队减少一个元素再次进行
void HeapSortSmallToBig(HPDataType* a, int size)
{
    assert(a);

    //size代表数组大小
    //因为size在进行建堆调整的时候会发生变化,所以要记录下来以便输出
    int start_size = size;
    //给进来一个数组,现将该数组进行建堆
    for (int i = (size - 2) / 2; i >= 0; i--)
    {
        ADJustDown(a, size, i);
    }

    //当size变为0的时候,那么该堆已经变为了一个有序的序列了
    while (size > 1)
    {
        Swap(&a[0], &a[size - 1]);
        size--;

        //对于新的堆进行调整,时间复杂度O(logN)
        ADJustDown(a, size, 0);
    }
    //该函数排序函数的时间复杂度为O(NlogN)
    //调整完之后size变为了0,对于这个堆,size还应该是原来的size
    size = start_size;
}

这就是采用堆排序,将数组元素进行非递减排序。
对于从大到小采用小顶堆来进行排序。
接下来看一下非递增排序。其实非递增排序和非递减排序思想差不多。

非递增排序就是,先将数组变成一个小顶堆,然后也是将堆顶元素与最后一个元素一次交换,在进行小顶堆的调整。直到最后在只剩下一个堆顶元素,那么数组也就排序完成了。

如图:
初始化
建成小顶堆:
小顶堆
交换第一次:
交换第一次
调整第一次;
调整第一次
再交换,再调整:
交换第二次
调整第二次
在交换,再调整:
交换第三次
调整第三次
再交换,再调整:
交换第四次
调整第四次
再交换:
交换第五次
因为只剩最后一个结点了,所以不在需要进行调整了,数组已经变成了一个非递增的序列了。

代码:

//小顶堆从上到下,调整
void ADJustDownSmallHeap(HPDataType* a, int size, int parent)
{
    assert(a);

    //左孩子结点
    int child = parent * 2 + 1;
    //左孩子结点的下标小于数组的最大下标时,堆是一个完全二叉树
    while (child < size)
    {
        //取出最大的孩子结点,只有当右孩子存在时,才能求两个中较小的一个孩子
        if (a[child + 1] < a[child] && ((child + 1) < size))
        {
            child++;
        }

        //交换结点,向下调整
        if (a[parent] > a[child])
        {
            Swap(&a[parent], &a[child]);
            parent = child;
            child = parent * 2 + 1;
        }

        //如果要调整的的这个结点,比起的孩子结点大的话,那就不用调整了
        else
        {
            break;
        }
    }
}


//对数组进行排序(从大到小) 采用小顶堆
void HeapSortBigToSmall(HPDataType* a, int size)
{
    assert(a);

    int start_size = size;
    //先将该数组变为一个小顶堆
    for (int i = (size - 2) / 2; i >= 0; i--)
    {
        ADJustDownSmallHeap(a, size, i);
    }

    while (size > 1)
    {
        Swap(&a[0], &a[size - 1]);
        size--;

        //每次交换之后就要向下调整
        ADJustDownSmallHeap(a, size, 0);
    }
}

这就是堆排序了。
最后再介绍一下对排序的优点:关键就是效率高。
适合大数据的排序。
比如有100亿的数据,让你进行排序的话,选择排序的时间复杂度为O(n²),那么次数为10亿*10亿
而堆排序的时间复杂度为O(nlogn),则其次数为10亿*30。这就整整减少了可以说是将近70亿的次数,这是多么的效率高。
因为排序的时候的时候要用到存储空间这将是节省了多么大的空间。
所以对于堆排序要好好的学习,并且加以利用。

最后正好今天四六级成绩出来了!小凯祝大家四六级都过!不用再转发杨超越,相信自己。

每天都要好好的学习英语哦!

展开阅读全文

没有更多推荐了,返回首页