本文主要介绍堆排序算法(HeapSort),堆排序像合并排序而不像插入排序,堆排序的运行时间为O(nlgn);像插入排序而不像合并排序,它是一种原地(in place)排序算法。在任何时候,数组中只有常数个元素存储在输入数组以外,这样,堆排序就把插入排序和合并排序的优点结合起来。

堆排序还引入了另外一种算法设计技术,利用某种数据结构(在此算法中为“堆”)来管理算法执行中的信息。堆数据结构不只在堆排序算法中有用,还可以构成一个有效的优先队列。堆数据结构是一种数组对象,它可以被视为一颗完全二叉树,树种每个结点与数组中存放该结点值的那个元素对应。树的每一层都是填满的,最后一层除外(最后一层从一个结点的左子树开始填)。

堆数据结构在最大(最小)优先级队列中得到有效的应用,本文以大顶堆为例,详细给出堆排序算法的实现以及最大级优先队列的算法实现。

git代码

GitHub chapter 6 程序代码下载

堆数据结构

堆数据结构是一种数组抽象,它可以被看做一颗完全二叉树,树中每个结点与数组中存放该结点值的那个元素对应。除了堆顶根节点之外,所有树结点都有父节点,同理,除了叶子结点外,所有树结点都至少有一个儿子结点。

//求左儿子结点
int Left(int i)
{
return 2 * i;
} //求右儿子结点
int Right(int i)
{
return 2 * i + 1;
} //求父节点
int Parent(int i)
{
return i / 2;
}

在堆排序算法中,有以下几个关键步骤:

MaxHeapify过程,其运行时间为O(logn),是保持最大堆性质的关键;

BuildMaxHeap过程,建立大顶堆,在无序的原始数组中构造出一颗大顶堆;

HeapSort过程,对大顶堆从最后一个叶子结点开始处理排序,得到一组升序排列的序列。

堆排序算法实现

(1) MaxHeap.h

#ifndef MAX_HEAP_H
#define MAX_HEAP_H /*
* 大顶堆 MaxHeap 数据结构
*/ #include <iostream> using namespace std;
class MaxHeap
{
public:
MaxHeap(int *data, int size);
~MaxHeap();
void maxHeapSort();
void displayList(int size)
{
for (int i = 0; i < size; i++)
cout << m_dataList[i] << "\t";
cout << endl;
}
private:
int *m_dataList;
int m_heapSize; void buildMaxHeap(int *data);
void maxHeapify(int *data, int i); int Left(int i)
{
return 2 * i;
} int Right(int i)
{
return 2 * i + 1;
} void swap(int &a, int &b)
{
int temp = a;
a = b;
b = temp;
}
};
#endif

(2)MaxHeap.cpp

#include "MaxHeap.h"
#include <iostream> using namespace std; MaxHeap::MaxHeap(int *data, int size) :m_dataList(data), m_heapSize(size)
{} MaxHeap::~MaxHeap()
{
delete m_dataList;
} void MaxHeap::buildMaxHeap(int *data)
{
for (int i = m_heapSize / 2; i >= 1; i--)
{
maxHeapify(data, i);
}
} void MaxHeap::maxHeapify(int *data, int i)
{
int largest;
int left = Left(i);
int right = Right(i); if (left <= m_heapSize && data[left-1] > data[i-1])
largest = left;
else
largest = i; if (right <= m_heapSize && data[right-1] > data[largest-1])
largest = right; if (largest != i)
{
swap(data[i - 1], data[largest - 1]);
maxHeapify(data, largest);
}
} void MaxHeap::maxHeapSort()
{
buildMaxHeap(m_dataList);
int length = m_heapSize;
for (int i = length; i >= 1; i--)
{
swap(m_dataList[i - 1], m_dataList[0]);
m_heapSize--;
maxHeapify(m_dataList, 1);
} }

(3)main.cpp

#include "MaxHeap.h"
#include <iostream>
#include <cstdlib>
#include <ctime> using namespace std; const int N = 10;
int main()
{
//声明一个待排序数组
int array[N];
//设置随机化种子,避免每次产生相同的随机数
srand(time(0));
for (int i = 0; i<N; i++)
{
array[i] = rand() % 101;//数组赋值使用随机函数产生1-100之间的随机数
} //调用堆排序函数对该数组进行排序
MaxHeap *maxHeap = new MaxHeap(array, N);
cout << "排序前:" << endl;
maxHeap->displayList(N); maxHeap->maxHeapSort();
cout << endl << "排序后:" << endl;
maxHeap->displayList(N);
system("pause");
return 0;
}

测试结果:

最大优先级队列

优先级队列是一种用来维护一组元素构成的集合元素的数据结构,这一组元素中的每一个都有关键字key,分为最大优先级队列和最小优先级队列两种。

本章集中讨论了基于最大堆实现的最大优先级队列。

一个最大优先级队列有以下关键操作:

Insert(x) : 将关键字x插入集合,并保持集合的性质;

MaxiMum():返回集合中最大关键字的元素,对于最大优先级队列,队头元素即是最大关键字元素;

ExtractMax() : 去掉并返回队列集合中最大关键字的元素;

IncreaseKey(x , k) : 将元素位置x处的关键字值增加到k , k 本值不可小于x位置元素的初始值;

算法实现:

(1) MaxPriQueue.h

#ifndef _MAXPRIQUEUE_H_
#define _MAXPRIQUEUE_H_ /*
* 最大堆实现的最大优先级队列数据结构
*/
#include <iostream>
using namespace std; class MaxPriQueue{ public: MaxPriQueue(int *data, int size);
~MaxPriQueue(); //将元素x插入该优先级队列
void insert(int x); //去掉并返回队列中具有最大关键字的元素
int extractMax(); //将元素x的关键字的值增加到k
void increaseKey(int x, int k); void maxHeapify(int *data, int i); int MaxiMum()
{
return m_dataList[0];
} void displayList()
{
for (int i = 0; i < m_queueSize; i++)
cout << m_dataList[i] << "\t";
cout << endl;
} private:
int *m_dataList;
int m_queueSize; //优先级当前长度 //求左儿子结点
int Left(int i)
{
return 2 * i;
} //求右儿子结点
int Right(int i)
{
return 2 * i + 1;
} //求父节点
int Parent(int i)
{
return i / 2;
} void swap(int &a, int &b)
{
int temp = a;
a = b;
b = temp;
} void buildMaxHeap(int *data);
}; #endif

(2) MaxPriQueue.cpp

#include "MaxPriQueue.h"
#include <iostream> using namespace std; //构造函数
MaxPriQueue::MaxPriQueue(int *data, int size) :m_dataList(data), m_queueSize(size)
{
//构建大顶堆
buildMaxHeap(m_dataList);
} MaxPriQueue::~MaxPriQueue()
{
delete m_dataList;
} void MaxPriQueue::buildMaxHeap(int *data)
{
//从最后一个根元素自底向上调整
for (int i = m_queueSize / 2; i >= 1; i--)
maxHeapify(data, i);
} int MaxPriQueue::extractMax()
{
if (m_queueSize < 1)
return -1; int max = m_dataList[0]; m_dataList[0] = m_dataList[m_queueSize - 1];
m_queueSize--; maxHeapify(m_dataList, 1); return max; } void MaxPriQueue::increaseKey(int x, int k)
{
if (k < m_dataList[x-1])
return; m_dataList[x-1] = k; //自底向上调整大顶堆
while (x > 1 && m_dataList[Parent(x)-1] < m_dataList[x-1])
{
swap(m_dataList[Parent(x)-1], m_dataList[x-1]);
x = Parent(x);
}
} void MaxPriQueue::insert(int x)
{
m_queueSize++;
m_dataList[m_queueSize - 1] = x-1;
increaseKey(m_queueSize, x);
} void MaxPriQueue::maxHeapify(int *data, int i)
{
int largest;
int left = Left(i);
int right = Right(i); if (left <= m_queueSize && data[left - 1] > data[i - 1])
largest = left;
else
largest = i; if (right <= m_queueSize && data[right - 1] > data[largest - 1])
largest = right; if (largest != i)
{
swap(data[i - 1], data[largest - 1]);
maxHeapify(data, largest);
}
}

(3) main.cpp

#include "MaxPriQueue.h"
#include <iostream>
#include <cstdlib>
#include <ctime> using namespace std; const int N = 10;
int main()
{
//声明一个待排序数组
int array[N];
//设置随机化种子,避免每次产生相同的随机数
srand(time(0));
for (int i = 0; i<N; i++)
{
array[i] = rand() % 101;//数组赋值使用随机函数产生1-100之间的随机数
} //调用堆排序函数对该数组进行排序
MaxPriQueue *maxPriQueue = new MaxPriQueue(array, N); cout << "大顶堆:" << endl;
maxPriQueue->displayList(); cout << "堆顶元素" << maxPriQueue->MaxiMum() << endl; cout << "删除最大元素" << maxPriQueue->extractMax() << endl; cout << "新的大顶堆:" << endl;
maxPriQueue->displayList(); cout << "将队头元素增加至100." << endl;
maxPriQueue->increaseKey(1, 100);
cout << "新的大顶堆:" << endl;
maxPriQueue->displayList(); system("pause");
return 0;
}

测试结果:

《算法导论》— Chapter 6 堆排序的更多相关文章

  1. 堆排序与优先队列——算法导论(7)

    1. 预备知识 (1) 基本概念     如图,(二叉)堆是一个数组,它可以被看成一个近似的完全二叉树.树中的每一个结点对应数组中的一个元素.除了最底层外,该树是完全充满的,而且从左向右填充.堆的数组 ...

  2. (搬运)《算法导论》习题解答 Chapter 22.1-1(入度和出度)

    (搬运)<算法导论>习题解答 Chapter 22.1-1(入度和出度) 思路:遍历邻接列表即可; 伪代码: for u 属于 Vertex for v属于 Adj[u] outdegre ...

  3. 《算法导论》 — Chapter 7 高速排序

    序 高速排序(QuickSort)也是一种排序算法,对包括n个数组的输入数组.最坏情况执行时间为O(n^2). 尽管这个最坏情况执行时间比較差.可是高速排序一般是用于排序的最佳有用选择.这是由于其平均 ...

  4. 《算法导论》— Chapter 9 中位数和顺序统计学

    序 在算法导论的第二部分主要探讨了排序和顺序统计学,第六章~第八章讨论了堆排序.快速排序以及三种线性排序算法.该部分的最后一个章节,将讨论顺序统计方面的知识. 在一个由n个元素组成的集合中,第i个顺序 ...

  5. 《算法导论》 — Chapter 8 线性时间排序

    序 到目前为止,关于排序的问题,前面已经介绍了很多,从插入排序.合并排序.堆排序以及快速排序,每一种都有其适用的情况,在时间和空间复杂度上各有优势.它们都有一个相同的特点,以上所有排序的结果序列,各个 ...

  6. 《算法导论》 — Chapter 7 快速排序

    序 快速排序(QuickSort)也是一种排序算法,对包含n个数组的输入数组,最坏情况运行时间为O(n^2).虽然这个最坏情况运行时间比较差,但是快速排序通常是用于排序的最佳实用选择,这是因为其平均性 ...

  7. 《算法导论》— Chapter 15 动态规划

    序 算法导论一书的第四部分-高级设计和分析技术从本章开始讨论,主要分析高效算法的三种重要技术:动态规划.贪心算法以及平摊分析三种. 首先,本章讨论动态规划,它是通过组合子问题的解而解决整个问题的,通常 ...

  8. 《算法导论》— Chapter 11 散列表

    1 序 在很多应用中,都要用到一种动态集合结构,它仅支持INSERT.SEARCH以及DELETE三种字典操作.例如计算机程序设计语言的编译程序需要维护一个符号表,其中元素的关键字为任意字符串,与语言 ...

  9. 基本数据结构(2)——算法导论(12)

    1. 引言     这一篇博文主要介绍链表(linked list),指针和对象的实现,以及有根树的表示. 2. 链表(linked list) (1) 链表介绍      我们在上一篇中提过,栈与队 ...

  10. 算法导论第十八章 B树

    一.高级数据结构 本章以后到第21章(并查集)隶属于高级数据结构的内容.前面还留了两章:贪心算法和摊还分析,打算后面再来补充.之前的章节讨论的支持动态数据集上的操作,如查找.插入.删除等都是基于简单的 ...

随机推荐

  1. 跟我一起玩Win32开发(11):使用控件——先来耍一下按钮

    用户通过控件与应用程序交互,在吹牛之前,先介绍一个工具,这是官方的工具,使用它,你可以预览常用控件的外观.样式,以及对控进行操作时接收和发送哪些消息.下载地址如下: http://www.micros ...

  2. 跟我一起玩Win32开发(8):绘图(A)

    从本篇开始,我就不吹牛皮,那就吹吹兔皮吧.说说与绘图有关的东东. 要进行绘制,首先要得到一个DC,啥是DC呢?按字面翻译叫设备上下文,也可以翻译为设备描述表,它主要指API为我们封装了一些与显示设备相 ...

  3. April Fools Contest 2017 E

    Description Input The input consists of four lines, each line containing a single digit 0 or 1. Outp ...

  4. 微信小程序开发常见的拉起外部地图软件进行导航的功能

    <view class="dh" bindtap="mapNavigation" data-addr="{{address}}"> ...

  5. Win7系统出现提示: “Windows已遇到关键问题,将在一分钟后自动重新启动。”

    1. 若用户在使用Win7系统时,遇到上述系统故障,建议重启电脑.等电脑开机自检一过,马上按键盘上的F8键,选择进入安全模式.在安全模式下,进行系统还原.其他的解决方法见下. 1.或者,在安全模式下, ...

  6. 用python编写的excel拆分小工具

    from datetime import date,datetime from openpyxl import Workbook from openpyxl import load_workbook ...

  7. H5页面快速搭建之高级字体应用实践

    原文出处: 淘宝前端团队(FED)- 龙驭 背景 最近在开发一个 H5 活动页快速搭建平台,可以通过拖拽编辑图片,文字等元素组件,快速搭建出一个移动端的活动页面,基本交互和成品效果类似 PPT 软件. ...

  8. Retrofit Upload multiple files and parameters

    Retrofit 的介绍以及基本使用 这里不再说明. 关于多文件上传 以及上传文件的同时携带多个参数说明 网上涉及到的不是太多. 上一张帅图: 代码: apiService: /** params 参 ...

  9. sass 常用用法笔记

    最近公司开发的h5项目,需要用到sass,所以领导推荐让我去阮一峰大神的SASS用法指南博客学习,为方便以后自己使用,所以在此记录. 一.代码的重用 1.继承:SASS允许一个选择器,继承另一个选择器 ...

  10. 在colab上运行style-transfer

    1,  打开chrome浏览器,输入以下网址,打开风格转换主文件 https://colab.research.google.com/github/Hvass-Labs/TensorFlow-Tuto ...