数据结构堆(包含堆排序)有这么简单吗? 其实是真的简单!

前言

今天无意中翻了一下以前写的堆排序文章,发现写的也太Low了 . . .所以准备重新写一篇文章来更详细的讲解堆排序,主要参考啊哈算法 和以前的一些资料,希望大家喜欢,今天也是放假的日子,但我还是选择在学校留上几天,希望大家喜欢这一篇文章 ^ _ ^ . . .

相关树及C++堆排序一些文章:

1)《算法笔记》—— 堆排序算法( C++实现)
2) 二叉搜索树 —— 查找与排序
3) 红黑树基本功能 —— C++实现
4) 二叉树(链式存储)—— C++实现
5) 二叉树(顺序存储)—— C++实现

下面我将更详细的讲解堆排序 . . .
.


前面会讲一些堆的一些特性 ~

文章目录:

1)删除顶元素并且添加新元素
2)增加新元素
3)创建一个堆(关键的部分,如何创建),堆排序


在这里插入图片描述

那么什么是堆排序呢? 让我们看看度娘的解释:

—— “堆排序(英语:Heapsort)是指利用堆这种数据结构所设计的一种 排序算法
堆是一个近似完全二叉树的结构,并同时满足堆积的性质:即 子结点的键值或索引总是小于(或者大于)它的父节点。”

首先,它是一种排序算法,然后它拥有的一些特性 . . . 下面让我们来了解一下这个特性是什么样子的,走你 ~

.

堆是一种特殊的完全二叉树,就像下面这棵树一样:
在这里插入图片描述

这棵二叉树有这么一个特性:所有父结点都比子结点要小
那么这个特性有什么用呢?—— 下面我们将讲解堆的一些优秀的特性 . . .
.

一)删除顶元素并且添加新元素

我们现在需要删除堆顶部的元素,然后将一个数23放到堆顶,我们将23调整到合适的位置。那们我们如何调整呢?当前结果为如下的图:
在这里插入图片描述

当然,聪明的你们肯定想到了, 向下调整!! 我们将 23 与 它的两个儿子(2,5)进行比较,与较小的一个互换位置,结果如下:
在这里插入图片描述

同理,我们依次重复的比较,将 23调整到最终的位置,包含它的移动路线:
在这里插入图片描述

我们将这个 23进行调整,竟然只进行了 3次比较,是不是非常的快速呢? 现在最小的数在堆顶(2)。

效率提升的不是一星半点,当前我们肯定需要一个东西来进行比较,比如我们有 14个数字,分别是99、5、36、7、22、17、46、12、2、19、25、28 和 92,假如我们删除最小的数后再添加一个新数,并且这样的操作进行 14次,那么它的时间复杂度将为 O(14 ^2)即 O( N ^2) . . .

假如现在我们有 1Y 个数,对它进行删除最小数并且 新增一个数的操作(进行1Y次),我们只需要 2.7秒,它的复杂度为 O(logN) . . .

结点数据向下调整的代码如下:
在这里插入图片描述
下面我会展示出完整的代码示例,这个图片样式的仅供参考学习 . . .

.

二)增加新元素

如果我们想直接将新元素插入到堆之中,只需要将新元素放入到末尾,再将新元素向上调整,直至满足堆的特性为止 . . . 时间复杂度为 O(logN),例如我们现在需要新增一个元素为 3,它的运动轨迹如下所示:
在这里插入图片描述

最终效果如下所示:
在这里插入图片描述
结点数据向上调整的代码如下:
在这里插入图片描述
.

三)创建一个堆(关键的部分,如何创建)

首先,我们将数据依次的放入到堆中,然后设计一个算法将其中的数据进行调整,以便其满足堆的特性(最小堆或者最大堆) . . .

将数据依次放入到堆中:

n = 0;
for(i = 1; i <= m; i++)
{
    ++n;
    h[n] = a[i];
}

堆中数据进行调整:

for(i = n / 2; i >= 1; --i)
    siftdown(i);

这两行代码是什么意思呢? 为什么 i 要从 n / 2 开始呢?
因为我们调整堆中的数据满足堆特性,只需要将每个根结点向下调整就行了,而最后叶子结点是没有儿子结点的,所以我们只需要从倒数第二排开始即可,如下所示:
在这里插入图片描述

—— 堆排序
我们用这样的思想就可以把一个堆调整好了,然后就可以使用我们的堆排序了,堆排序的时间复杂度是 O(NlogN),它与快速排序是一样的实现堆排序我们只需要每次删除顶部元素并将顶部元素输出,然后用最后一个元素赋值给顶结点,并且将这个新的顶点进行向下调整即可 . . .

删除最大的元素代码如下所示(长度 - 1,假如是最小堆):

int deletemax()
{
    int t;
    t = h[1];		// 用于返回的值
    h[1] = h[n];	// 最后一个元素赋值给顶结点
    --n;			// 长度 - 1
    siftdown(1);	// 向下调整(调整最小值到上面去)
    return t;		// 返回的数据
}

建堆以及堆排序的完整代码如下所示:
在这里插入图片描述在这里插入图片描述
当我们输入下面的这些数据后:

14
99 5 36 7 22 17 46 12 2 19 25 28 1 92

运行结果是:

1 2 5 7 12 17 19 22 25 28 36 46 92 99

.
当然我们还有更好的办法,就是我们刚开始不是创建的最小堆,而是创建的是最大堆,最大堆创建好之后,最大的元素在 h[1],我们需要的是将数据从小到大进行排序,大的数据放到最后面。因此,我们每次将最大值放到最后面,并且长度 - 1,这样获得的最大值就不会被影响到,然后我们利用与最大值交换的那个最小值进行向下调整,重复如此直至n的值为0 . . .

代码设计如下所示:

// 堆排序
void heapsort(){
    while(n > 1){
        swap(1, n);
        --n;
        siftdown(1);
    }
}

这样我们只需要在 main函数中调用这个方法之后,直接输出堆中的数据即可:

// 堆排序
heapsort();

// 输出
for(i = 1; i <= num; ++i)
    printf("%d ", h[i]);

结果与上面是一样的 . . .
.
.
.


浪子花梦

一个有趣的程序员 ~

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章