最大堆(MaxHeap)
性质
- 二叉堆是一颗完全二叉树,而完全二叉树是把元素排列成树的形状。
- 堆中某个节点的值总不大于其父节点的值最大堆(相应的可以定于最小堆)

// 返回完全二叉树的数组表示中,一个索引所表示的元素的父亲节点的索引
constexpr int parent(const int index) const {
if (index == 0) {
throw new NoParent();
}
return (index - 1) / 2;
}
// 返回完全二叉树的数组表示中,一个索引所表示的元素的左孩子节点的索引
constexpr int leftChild(const int index) const {
return (index * 2) + 1;
}
// 返回完全二叉树的数组表示中,一个索引所表示的元素的右孩子节点的索引
constexpr int rightChild(const int index) const {
return (index * 2) + 2;
}
可以先阅读底层动态数组Array
添加

首先我们堆中的数据使用数组排列的,所以添加一个元素就是在层序遍历的最右端,也就是最下面一层的最后添加一个元素。但是以数组来看就是在索引为10的地方添加一个元素。
void add(const T &e) {
data->addLast(e); //在数组的末尾添加元素
shiftUp(data->getSize() - 1); //上浮添加元素的索引
}
- 时间复杂度O(logn)

但是添加的元素不符最大堆的性质,索引我需要一些调整,而这个调整就是一个上浮的过程。
void shiftUp(int index) {
//如果传入索引小于等于0并且父元素大于等于子元素则停止循环
while (index > 0 && data->get(index) > data->get(parent(index))) {
data->swap(index, parent(index)); //位置交换
index = parent(index); //把父节点的索引给子节的
}
}
取出最大元素

最大堆的最大元素就是其根节点元素,取出的操作只能取出这个元素,对于数组来说,根结点就是索引为0的元素。

我们把堆中最后一个元素顶到堆顶去,然后再把最后一个元素删除。然而这样就又不符合最大堆的性质。

这样的话,其不大于它的子节点,此时又要进行调整,这个调整的过程叫做下沉。在这个过程中每次需要下沉的时候都要和它的两个孩子进行比较,选择其中较大的进行交换位置。

- 时间复杂度O(logn)
//返回最大的元素
T findMax() const {
if (data->isEmpty()) {
throw Empty();
}
return data->get(0);
}
//取出最大的元素
T extractMax() {
T ret = findMax();
data->swap(0, data->getSize() - 1);
data->removeLast();
shiftDown(0);
return ret;
}
//下沉
void shiftDown(int k) {
while (leftChild(k) < data->getSize()) {
int j = leftChild(k);
//j保存的是左右孩子中较大的元素索引
if (j + 1 < data->getSize() && data->get(j + 1) > data->get(j)) {
j = rightChild(k);
}
//如果子节点小于等于父节点了,就结束
if (data->get(k) > data->get(j)) {
break;
}
data->swap(k, j);
k = j;
}
}
取出堆中最大的元素,并替换成元素e
- 时间复杂度O(logn)
T replace(T e) {
T ret = findMax();
data->set(0, e);
shiftDown(0);
return ret;
}
Heapify
将n个元素逐个插入到一个空堆中,算法复杂度是O(nlogn),Heapify的过程,算法复杂度是O(n)。
MaxHeap(T arr[], const int n) {
data = new Array<T>(arr, n);
for (int i = parent(n - 1); i >= 0; --i) {
shiftDown(i);
}
}
对比使用与不适用Heapify代码
#include <iostream>
#include "MaxHeap.h"
#include <cassert>
template<typename T>
double testHeap(T testData[], int n, bool isHeapify) {
clock_t startTime = clock();
MaxHeap<T> *maxHeap;
if (isHeapify) {
maxHeap = new MaxHeap<T>(testData, n);
} else {
maxHeap = new MaxHeap<T>();
for (int i = 0; i < n; ++i) {
maxHeap->add(testData[i]);
}
}
T *arr = new T[n];
for (int j = 0; j < n; ++j) {
arr[j] = maxHeap->extractMax();
}
for (int k = 1; k < n; ++k) {
assert(arr[k - 1] >= arr[k]);
}
std::cout << "Test MaxHeap completed." << std::endl;
clock_t endTime = clock();
return double(endTime - startTime) / CLOCKS_PER_SEC;
}
int main() {
int n = 5000000;
int *testData = new int[n];
for (int i = 0; i < n; ++i) {
testData[i] = rand() % INT32_MAX;
}
double time1 = testHeap(testData, n, false);
std::cout << "Without heapify :" << time1 << " s " << std::endl;
double time2 = testHeap(testData, n, true);
std::cout << "With heapify :" << time2 << " s " << std::endl;
return 0;
}
代码清单
//
// Created by cheng on 2021/7/10.
//
#ifndef MAXHEAP_MAXHEAP_H
#define MAXHEAP_MAXHEAP_H
#include "Array.h"
template<typename T>
class MaxHeap {
public:
class NoParent {
};
class Empty {
};
MaxHeap() {
data = new Array<T>();
}
~MaxHeap() {
delete data;
data = nullptr;
}
MaxHeap(const int capacity) {
data = new Array<T>(capacity);
}
MaxHeap(T arr[], const int n) {
data = new Array<T>(arr, n);
for (int i = parent(n - 1); i >= 0; --i) {
shiftDown(i);
}
}
constexpr int getSize() const {
return data->getSize();
}
constexpr bool isEmpty() const {
return data->isEmpty();
}
// 返回完全二叉树的数组表示中,一个索引所表示的元素的父亲节点的索引
constexpr int parent(const int index) const {
if (index == 0) {
throw new NoParent();
}
return (index - 1) / 2;
}
void add(const T &e) {
data->addLast(e);
shiftUp(data->getSize() - 1);
}
//返回最大元素
T findMax() const {
if (data->isEmpty()) {
throw Empty();
}
return data->get(0);
}
//取出最大的元素
T extractMax() {
T ret = findMax();
data->swap(0, data->getSize() - 1);
data->removeLast();
shiftDown(0);
return ret;
}
//取出堆中最大的元素,并替换成元素e
T replace(T e) {
T ret = findMax();
data->set(0, e);
shiftDown(0);
return ret;
}
void print() {
data->print();
}
private:
void shiftDown(int k) {
while (leftChild(k) < data->getSize()) {
int j = leftChild(k);
if (j + 1 < data->getSize() && data->get(j + 1) > data->get(j)) {
j = rightChild(k);
}
if (data->get(k) > data->get(j)) {
break;
}
data->swap(k, j);
k = j;
}
}
void shiftUp(int index) {
while (index > 0 && data->get(index) > data->get(parent(index))) {
data->swap(index, parent(index));
index = parent(index);
}
}
// 返回完全二叉树的数组表示中,一个索引所表示的元素的左孩子节点的索引
constexpr int leftChild(const int index) const {
return (index * 2) + 1;
}
// 返回完全二叉树的数组表示中,一个索引所表示的元素的右孩子节点的索引
constexpr int rightChild(const int index) const {
return (index * 2) + 2;
}
private:
Array<T> *data;
};
#endif //MAXHEAP_MAXHEAP_H
最大堆(MaxHeap)的更多相关文章
- java——最大堆 MaxHeap
使用数组来实现最大堆 堆是平衡二叉树 import Date_pacage.Array; public class MaxHeap<E extends Comparable <E>& ...
- 数据结构-详解优先队列的二叉堆(最大堆)原理、实现和应用-C和Python
一.堆的基础 1.1 优先队列和堆 优先队列(Priority Queue):特殊的"队列",取出元素顺序是按元素优先权(关键字)大小,而非元素进入队列的先后顺序. 若采用数组或链 ...
- MyCat源码分析系列之——结果合并
更多MyCat源码分析,请戳MyCat源码分析系列 结果合并 在SQL下发流程和前后端验证流程中介绍过,通过用户验证的后端连接绑定的NIOHandler是MySQLConnectionHandler实 ...
- 数据结构图文解析之:二叉堆详解及C++模板实现
0. 数据结构图文解析系列 数据结构系列文章 数据结构图文解析之:数组.单链表.双链表介绍及C++模板实现 数据结构图文解析之:栈的简介及C++模板实现 数据结构图文解析之:队列详解与C++模板实现 ...
- 面试题目——《CC150》高等难题
面试题18.1:编写一个函数,将两个数字相加.不得使用+或其他算数运算符. package cc150.high; public class Add { public static void main ...
- 二叉堆(二)之 C++的实现
概要 上一章介绍了堆和二叉堆的基本概念,并通过C语言实现了二叉堆.本章是二叉堆的C++实现. 目录1. 二叉堆的介绍2. 二叉堆的图文解析3. 二叉堆的C++实现(完整源码)4. 二叉堆的C++测试程 ...
- 二叉堆(三)之 Java的实现
概要 前面分别通过C和C++实现了二叉堆,本章给出二叉堆的Java版本.还是那句话,它们的原理一样,择其一了解即可. 目录1. 二叉堆的介绍2. 二叉堆的图文解析3. 二叉堆的Java实现(完整源码) ...
- 3.Python3标准库--数据结构
(一)enum:枚举类型 import enum ''' enum模块定义了一个提供迭代和比较功能的枚举类型.可以用这个为值创建明确定义的符号,而不是使用字面量整数或字符串 ''' 1.创建枚举 im ...
- pat04-树9. Path in a Heap (25)
04-树9. Path in a Heap (25) 时间限制 150 ms 内存限制 65536 kB 代码长度限制 8000 B 判题程序 Standard 作者 CHEN, Yue Insert ...
- 萌新笔记之堆(heap)
前言(萌新感想): 以前用STL的queue啊stack啊priority_queue啊,一直很想懂原理,现在终于课上到了priority_queue,还有就是下周期中考,哈哈,所以写几篇blog总结 ...
随机推荐
- java打印杨辉三角
package com.dylan.practice.interview; /** * 打印杨辉三角 * * 原理 * 1.每个数等于它上方两数之和 * 2.第n行的数字有n个 * * @author ...
- ERROR 1820 (HY000): You must reset your password using ALTER USER statement
新安装好的mysql5.7数据库,用root登录以后执行操作报这个错. 解决方法: mysql> alter user 'root'@'localhost' identified by 'roo ...
- 【libGDX】ApplicationAdapter生命周期
1 前言 libGDX 中,用户自定义的渲染窗口需要继承 ApplicationAdapter 类,ApplicationAdapter 实现了 ApplicationListener 接口,但实 ...
- [超实用插件]在Visual Studio中查看EF Core查询计划
前言 EF Core是我们.NET开发中比较常用的一款ORM框架,今天我们分享一款可以直接在Visual Studio中查看EF Core查询计划调试器可视化工具(帮助开发者分析和优化数据库查询性能) ...
- 【LeetCode栈与队列#06】前K个高频元素(TopK问题),以及pair、priority_queue的使用
前 K 个高频元素 力扣题目链接(opens new window) 给定一个非空的整数数组,返回其中出现频率前 k 高的元素. 示例 1: 输入: nums = [1,1,1,2,2,3], k = ...
- 如何优化好UITableView,值得思考
如果你觉得 UITableViewDelegate 和 UITableViewDataSource 这两个协议中有大量方法每次都是复制粘贴,实现起来大同小异:如果你觉得发起网络请求并解析数据需要一大段 ...
- 【Azure 事件中心】在Azure Function App中消费Event Hub数据,时常出现EventReceiveError
问题描述 在Azure Function App中消费Event Hub数据,时常出现EventReceiveError:New receiver 'P3-00122a562-4fa4-7f3f-ad ...
- 机器学习策略篇:详解训练/开发/测试集划分(Train/dev/test distributions)
训练/开发/测试集划分 设立训练集,开发集和测试集的方式大大影响了或者团队在建立机器学习应用方面取得进展的速度.同样的团队,即使是大公司里的团队,在设立这些数据集的方式,真的会让团队的进展变慢而不是加 ...
- FeignClient 报错: A bean with that name has already been defined and overriding is disabled.
1. 错误信息 *************************** APPLICATION FAILED TO START *************************** Descript ...
- Android---Android 开发四大组件
Android 应用程序组件 应用程序组件是一个Android应用程序的基本构建块.这些组件由应用清单文件松耦合的组织.AndroidManifest.xml描述了应用程序的每个组件,以及他们如何交互 ...