我们利用最大堆可以实现数组从小到大的原址排序,利用最小堆的可以实现对数组从大到小的原址排序。

1  二叉堆的简单介绍:

最大堆与最小堆可以当作通过数组来实现的一个完全二叉树,除了最底层之外其它层都是满的,并且最底层也是从左到右填充的。在最大堆中,父结点的值大于或等于子结点的值;在最小堆中,父结点的值小于或等于子结点的值。

当堆的元素在数组中下标从1开始时,很容易计算出父结点/左子结点/右子结点的下标:当父结点的下标为 i 时,左孩子下标为2i, 右孩子的下标为2i+1;当孩子结点为j时,父结点的下标为 j/2。

但是呢,数组的下标都是从0开始的,所以呢,关于父结点与子结点的关系要稍微绕一下:当父结点的下标为 i 时,左孩子下标为2i+1, 右孩子的下标为2(i+1);当孩子结点为j时,父结点的下标为 (j-1)/2。

2 使用最大堆或最小堆实现排序

以使用最大堆进行从小到大的排序为例,假设最大堆使用长度为N的数组表示,数组下标为0(对应堆根结点)的值最大。因此呢,我们可以把数组下标为0的值与数组下标为N-1的值进行交换,并把堆的大小由N减小为N-1并维护最大堆的性质;接下来不断重复以上过程,直到堆的大小变为1为止。

说明几点:

1 堆排序为为原址排序,它不需要额外的内存空间;

2 通常使用最大堆实现从小到大的排序,使用最小堆来实现从大到小的排序;

3 堆排序不是稳定的排序算法;

4 堆排序的时间复杂度为O(NlogN);

代码如下:

 /***********************************************************************
* Copyright (C) 2019 Yinheyi. <chinayinheyi@163.com>
*
* This program is free software; you can redistribute it and/or modify it under the terms
* of the GNU General Public License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version. * Brief:
* Author: yinheyi
* Email: chinayinheyi@163.com
* Version: 1.0
* Created Time: 2019年05月07日 星期二 21时41分02秒
* Modifed Time: 2019年05月09日 星期四 22时12分52秒
* Blog: http://www.cnblogs.com/yinheyi
* Github: https://github.com/yinheyi
*
***********************************************************************/ // 堆使用一个数组表示, 它可以当作一个完全二叉树,除了最底层之外,其它层都是满的,
// 并且最底层也是从左到右填充的。
//
// 当堆的下标(数组的下标)从1开始时比较好计算。因为:
// 1. 当父结点为i时, 左孩子为2i, 右孩子为2i+1;
// 2. 当孩子结点的下标为j时,父结点的下标为j/2 (根结点为父结点);
// 3. 一个结点的下标为k时, 则它所有的深度为 floor(logK);
// 4. 当一个堆共n个元素时,它的高度为floor(logN).
//
// 1
// / \
// 2 3
// / \ / \
// 4 5 6 7
// / \ / \ / \ / \
// 8 9 10 11 12 13 14 15
//
//
// 但是呢,数组的下标都是从0开始的,所以呢,我们下代码时,还是需要从0开始,而此时:
// 1. 当父结点的下标为i时,左孩子为2i+1, 右孩子为2*(i+1)
// 2. 当孩子结点的下标为j时,父结点的下标为(j-1)/2.
//
// 0
// / \
// 1 2
// / \ / \
// 3 4 5 6
// / \ / \ / \ / \
// 7 8 9 10 11 12 13 14
//
//
//
/************************** 代码如下 ****************************/ // 定义三个宏,分别用于求左孩子/右孩子/父结点的下标。
#define LEFT(i) (((i) << 1) + 1)
#define RIGHT(i) (((i) + 1) << 1)
#define PARENT(i) (((i) - 1) >> 1) // 小于比较函数
bool less(int lhs, int rhs)
{
return lhs < rhs;
} // 大于比较函数
bool greate(int lhs, int rhs)
{
return lhs > rhs;
}
typedef bool (*Comp)(int, int); // 交换两个元素的值
static inline void swap(int& lhs, int & rhs)
{
int _nTemp = lhs;
lhs = rhs;
rhs = _nTemp;
} // 假设一个节点的左子树与右子树都满足堆的性质,而该节点不满足最大堆或最小堆的性质,该
// 函数实现调整节点位置,维护堆的性质。
// 输入参数分别表示: 堆的数组/数组长度/节点i的下标/表示比较的二元谓词
// 复杂度为O(logN).
void Heapify(int array[], int nLength_, int nIndex_, Comp CompFunc)
{
if (array == nullptr || nIndex_ >= nLength_ || nIndex_ < || CompFunc == nullptr)
return; int _nLeft = LEFT(nIndex_);
int _nRight = RIGHT(nIndex_); // 初始化最大值节点的下标;
int _nLargest = nIndex_;
if ( _nLeft < nLength_ && !CompFunc(array[_nLargest], array[_nLeft]))
{
_nLargest = _nLeft;
}
if (_nRight < nLength_ && !CompFunc(array[_nLargest], array[_nRight]))
{
_nLargest = _nRight;
} /* 此时不需要维护堆的性质,直接返回 */
if (_nLargest == nIndex_)
{
return;
} swap(array[nIndex_], array[_nLargest]);
Heapify(array, nLength_, _nLargest, CompFunc);
} // 使用一个数组建立一个最小堆或最大堆。
// 输入参数为:一维数组/数组的长度/表示比较的二元谓词,用于控制建最小堆还是最大堆
// 复杂度为O(N).
void BulidHeap(int array[], int nLength_, Comp CompFunc)
{
if (array == nullptr || nLength_ <= || CompFunc == nullptr)
return; // 从最后一个元素的父节点开始调用Heapify()函数来建堆。
for (int i = PARENT(nLength_ - ); i >=; --i)
{
Heapify(array, nLength_, i, CompFunc);
}
} // 堆排序的函数
// 说明:1. 通过建立[最大堆]可以实现[从小到大]的排序;
// 2. 通过建立[最小堆]可以实现[从大到小]的排序
// 3. 堆排序为原址排序,它不需要借助额外的空间.(归并排序不是原址排序)
// 4. 堆排序的复杂度为O(NlogN).
//
// 堆排序的思想 (以从小到大排序说明):
// 第一步:建立一个最大堆;
// 第二步:把首元素与最后一个元素进行交换;
// 第三步:把堆的大小减1,对新的堆进行维护维的性质;
// 第四步:把首元素与倒数第二个元素进行交换;
// 第五步:把堆的大小减1,对新的堆进行维护维的性质;
// .......
//
void HeapSort(int array[], int nLength_, Comp CompFunc)
{
if (array == nullptr || nLength_ <= || CompFunc == nullptr)
return; BulidHeap(array, nLength_, CompFunc);
for (int i = nLength_; i >= ; /* 循环内 */) // i表示当前堆的大小
{
swap(array[], array[--i]);
Heapify(array, i, , CompFunc);
}
} /************ 测试 *****************/
#include <iostream> // 打印数组函数
void PrintArray(int array[], int nLength_)
{
if (nullptr == array || nLength_ <= )
return; for (int i = ; i < nLength_; ++i)
{
std::cout << array[i] << " ";
} std::cout << std::endl;
} // 主函数
int main(int argc, char* argv[])
{
int array[] = { , , , -, , , , , -, -}; PrintArray(array, );
HeapSort(array, , greate);
PrintArray(array, ); return ;
}

排序算法的c++实现——堆排序的更多相关文章

  1. 排序算法c语言描述---堆排序

    排序算法系列学习,主要描述冒泡排序,选择排序,直接插入排序,希尔排序,堆排序,归并排序,快速排序等排序进行分析. 文章规划: 一.通过自己对排序算法本身的理解,对每个方法写个小测试程序.具体思路分析不 ...

  2. 排序算法(三)堆排序及有界堆排序Java实现及分析

    1.堆排序基数排序适用于大小有界的东西,除了他之外,还有一种你可能遇到的其它专用排序算法:有界堆排序.如果你在处理非常大的数据集,你想要得到前 10 个或者前k个元素,其中k远小于n,它是很有用的. ...

  3. 排序算法<No.5>【堆排序】

    算法,是系统软件开发,甚至是搞软件的技术人士的核心竞争力,这一点,我坚信不疑.践行算法实践,已经有一段时间没有practise了,今天来一个相对麻烦点的,堆排序. 1. 什么是堆(Heap) 这里说的 ...

  4. 排序算法Java实现(堆排序)

    算法描述:对于给定的n个记录,初始时把这些记录看作一棵顺序存储的二叉树,然后将其调整为一个大顶堆,然后将堆的最后一个元素与堆顶元素进行交换后,堆的最后一个元素即为最大记录:接着将前(n-1)个元素重新 ...

  5. 排序算法C语言实现——堆排序

    /*堆排nlog(n)*//*堆排复杂度分析1.建堆((n*log(n))/2)    循环n/2次,每次调用HeapAdjust函数    HeapAdjust内部循环log(n)2.调整堆(((n ...

  6. java排序算法(三):堆排序

    java排序算法(三)堆排序 堆积排序(HeapSort)是指利用堆积树这种结构所设计的排序算法,可以利用数组的特点快速定位指定索引的元素.堆排序是不稳定的排序方法.辅助空间为O(1).最坏时间复杂度 ...

  7. Java各种排序算法详解

    排序大的分类可以分为两种:内排序和外排序.在排序过程中,全部记录存放在内存,则称为内排序,如果排序过程中需要使用外存,则称为外排序.下面讲的排序都是属于内排序. 内排序有可以分为以下几类: (1).插 ...

  8. 各种排序算法的分析及java实现

    排序一直以来都是让我很头疼的事,以前上<数据结构>打酱油去了,整个学期下来才勉强能写出个冒泡排序.由于下半年要准备工作了,也知道排序算法的重要性(据说是面试必问的知识点),所以又花了点时间 ...

  9. (转)各种排序算法的分析及java实现

    转自:http://www.cnblogs.com/liuling/p/2013-7-24-01.html 排序一直以来都是让我很头疼的事,以前上<数据结构>打酱油去了,整个学期下来才勉强 ...

随机推荐

  1. 第05组 Beta冲刺(1/4)

    第05组 Beta冲刺(1/4) 队名:天码行空 组长博客连接 作业博客连接 团队燃尽图(共享): GitHub当日代码/文档签入记录展示(共享): 组员情况: 组员1:卢欢(组长) 过去两天完成了哪 ...

  2. 三天精通Vue教程

    在这里更新作为后端工程师想要快速掌握Vue需要看的重点内容,三天精通教程,加油! 学前摘要 ES6的常用语法 Vue的常用语法

  3. Spring Boot 2.2.1 发布,一个有点坑的版本!

    上一篇:Spring Boot 2.2.0 正式发布,支持 JDK 13! Spring Boot 2.2.0 没发布多久,Spring Boot 2.2.1 又发布了,这是一个很有意思,又有点 &q ...

  4. 【06月10日】A股ROE最高排名

    个股滚动ROE = 最近4个季度的归母净利润 / ((期初归母净资产 + 期末归母净资产) / 2). 查看更多个股ROE最高排名 兰州民百(SH600738) - ROE_TTM:86.45% - ...

  5. 【2019年05月21日】A股ROE最高排名

    个股滚动ROE = 最近4个季度的归母净利润 / ((期初归母净资产 + 期末归母净资产) / 2). 查看更多个股ROE最高排名. 兰州民百(SH600738) - 滚动ROE:86.45% - 滚 ...

  6. Docker 一步搞定 ZooKeeper 集群的搭建

    Docker 一步搞定 ZooKeeper 集群的搭建 背景 原来学习 ZK 时, 我是在本地搭建的伪集群, 虽然说使用起来没有什么问题, 但是总感觉部署起来有点麻烦. 刚好我发现了 ZK 已经有了 ...

  7. unable to find utility "simctl", not a developer tool or in PATH解决方案

    解决方案就是去xcode设置里面,将Command line Tools设置一下,在Xcode>preferences>Locations里面,设置之后再运行终端即可

  8. VisualStudio ------- vs发布软件

    上线的系统和自己做的系统有什么区别 上线的没有源代码,没有实体层,数据库访问层  业务逻辑层 只有表现层  而且也也没有    .cs 和 .psd   文件,这样就不能修改系统代码 他们都在 Web ...

  9. OpenCV使用CMake和MinGW的编译安装

    官方教程:https://wiki.qt.io/How_to_setup_Qt_and_openCV_on_Windows 软件环境: Qt:5.11 CMake-3.14.4 OpenCV-4.1. ...

  10. Springboot Actuator之九:actuator jmx endpoint

    1.配置 endpoints.jmx.domain: myapp endpoints.jmx.uniqueNames: true endpoints.auditevents.enabled: true ...