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

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. springCloud值Eureka

    Spring Cloud特点 约定优于配置 开箱即用.快速启动 适用于各种环境      PC Server  云环境  容器(Docker) 轻量级的组件  服务发现Eureka 组件的支持很丰富, ...

  2. ABP 报错 System.InvalidOperationException: 实例失败。

    错误:System.InvalidOperationException: 实例失败. 解决:数据库链接写错了,斜杠的问题 core:两根斜杠 .net framework:一根斜杠 core的写法: ...

  3. cad.net 图层隐藏 IsHidden 用法 eDuplicateRecordName 报错

    提要:影响图层显示的主要有:关闭        isOff冻结        IsFrozen 图层隐藏 isHidden视口冻结 FreezeLayersInViewport 今天小博发现了一件事情 ...

  4. 关于Linux TCP "SACK PANIC" 远程拒绝服务漏洞的修复

    Linux 内核被曝存在TCP "SACK PANIC" 远程拒绝服务漏洞(漏洞编号:CVE-2019-11477,CVE-2019-11478,CVE-2019-11479),攻 ...

  5. nginx mysqlnd驱动引擎提升mysql性能

    nginx mysqlnd驱动引擎提升mysql性能 前期要安装 mysql mysqli pdo_mysql libiconv 1 先去PHP官网下个 php-5.6.29.tar.gz wget ...

  6. jquery swipper插件的一些弊端

    jquery swipper插件的一些弊端touch触摸机制是swipper的 阻止click冒泡.拖动Swiper时阻止click事件.下面这个方法或许可以解决触摸机制的问题 <pre> ...

  7. [转帖]Java Netty简介

    Java Netty简介 https://www.cnblogs.com/ghj1976/p/3779820.html Posted on 2014-06-10 13:41 蝈蝈俊 阅读(2992) ...

  8. drf面试题及总结

    drf面试题及总结 1.什么是前后端分离 2.什么是restful规范 3.模拟浏览器进行发送请求的工具 4.查找模板的顺序 5.什么是drf组件 6.drf组件提供的功能 7.drf继承过哪些视图类 ...

  9. golang ----并发 && 并行

    Go 语言的线程是并发机制,不是并行机制. 那么,什么是并发,什么是并行? 并发是不同的代码块交替执行,也就是交替可以做不同的事情. 并行是不同的代码块同时执行,也就是同时可以做不同的事情. 举个生活 ...

  10. 系统压测结果对比:tomcat/thinkphp/swoole/php-fpm/apache

    [测试所用服务器8核,16G内存]压测接口:很简单,从一张表里根据主键随机查询出一条数据[数据库服务器和WEB服务器分开的].表数据量大概:910000+条. 这个测试结果很有趣:tp5.0和3.2性 ...