二叉堆的介绍

二叉堆是完全二元树或者是近似完全二元树,按照数据的排列方式可以分为两种:最大堆和最小堆。
最大堆:父结点的键值总是大于或等于任何一个子节点的键值;最小堆:父结点的键值总是小于或等于任何一个子节点的键值。示意图如下:

二叉堆一般都通过"数组"来实现。数组实现的二叉堆,父节点和子节点的位置存在一定的关系。有时候,我们将"二叉堆的第一个元素"放在数组索引0的位置,有时候放在1的位置。当然,它们的本质一样(都是二叉堆),只是实现上稍微有一点点的区别。

假设"第一个元素"在数组中的索引为 0 的话,则父节点和子节点的位置关系如下:

  • 索引为i的左孩子的索引是 (2*i+1)
  • 索引为i的右孩子的索引是 (2*i+2)
  • 索引为i的父结点的索引是 floor((i-1)/2)

假设"第一个元素"在数组中的索引为 1 的话,则父节点和子节点的位置关系如下:

  • 索引为i的左孩子的索引是 (2*i)
  • 索引为i的右孩子的索引是 (2*i+1)
  • 索引为i的父结点的索引是 floor(i/2)
二叉堆的实现
1. 基本定义
template <typename T>
class MaxHeap{private:T *mHeap;                 // 数据int mCapacity;            // 总的容量int mSize;                // 实际容量private:// 最大堆的向下调整算法void filterdown(int start, int end);// 最大堆的向上调整算法(从start开始向上直到0,调整堆)void filterup(int start);public:MaxHeap();MaxHeap(int capacity);~MaxHeap();// 返回data在二叉堆中的索引int getIndex(T data);// 删除最大堆中的dataint remove(T data);// 将data插入到二叉堆中int insert(T data);// 打印二叉堆void print();
};

MaxHeap是最大堆的对应的类。它包括的核心内容是"添加"和"删除",理解这两个算法,二叉堆也就基本掌握了。下面对它们进行介绍

2. 添加

假设在最大堆[90,80,70,60,40,30,20,10,50]种添加85,需要执行的步骤如下:

如图所示,当向最大堆中添加数据时:先将数据加入到最大堆的最后,然后尽可能把这个元素往上挪,直到挪不动为止!将85添加到[90,80,70,60,40,30,20,10,50]中后,最大堆变成了[90,85,70,60,80,30,20,10,50,40]。

在实现添加之前,先实现怎么将数据往上挪:

/*
最大堆的向上调整算法(从start开始向上直到0,调整堆)
注:数组实现的堆中,第N个节点的左孩子的索引值是(2N+1),右孩子的索引是(2N+2)。
参数说明:
start -- 被上调节点的起始位置(一般为数组中最后一个元素的索引)*/
template <typename T>
void MaxHeap<T>::filterup(int start)
{int c = start;            // 当前节点(current)的位置int p = (c-1)/2;          // 父(parent)结点的位置 T tmp = mHeap[c];         // 当前节点(current)的大小while(c > 0){if(mHeap[p] >= tmp)break;else{mHeap[c] = mHeap[p];c = p;p = (p-1)/2;   }       }mHeap[c] = tmp;
}

下面实现添加:

/*
将data插入到二叉堆中
返回值:0,表示成功-1,表示失败*/
template <typename T>
int MaxHeap<T>::insert(T data)
{// 如果"堆"已满,则返回if(mSize == mCapacity)return -1;mHeap[mSize] = data;        // 将"数组"插在表尾filterup(mSize);            // 向上调整堆mSize++;                    // 堆的实际容量+1return 0;
}

insert(data)的作用:将数据data添加到最大堆中。当堆已满的时候,添加失败;否则data添加到最大堆的末尾。然后通过上调算法重新调整数组,使之重新成为最大堆。

3. 删除

假设从最大堆[90,85,70,60,80,30,20,10,50,40]中删除90,需要执行的步骤如下:

如上图所示,当从最大堆中删除数据时:先删除该数据,然后用最大堆中最后一个的元素插入这个空位;接着,把插入的数据依次往下挪,直到剩余的数据变成一个最大堆。
从[90,85,70,60,80,30,20,10,50,40]删除90之后,最大堆变成了[85,80,70,60,40,30,20,10,50]。

注意:考虑从最大堆[90,85,70,60,80,30,20,10,50,40]中删除60,执行的步骤不能单纯的用它的子节点来替换;而必须考虑到"替换后的树仍然要是最大堆"!

在实现删除之前首先实现怎么将数据往下挪:

/* 最大堆的向下调整算法注:数组实现的堆中,第N个节点的左孩子的索引值是(2N+1),右孩子的索引是(2N+2)。参数说明:start -- 被下调节点的起始位置(一般为0,表示从第1个开始)end   -- 截至范围(一般为数组中最后一个元素的索引)*/
template <typename T>
void MaxHeap<T>::filterdown(int start, int end)
{int c = start;          // 当前(current)节点的位置int l = 2*c + 1;     // 左(left)孩子的位置T tmp = mHeap[c];    // 当前(current)节点的大小while(l <= end){// "l"是左孩子,"l+1"是右孩子if(l < end && mHeap[l] < mHeap[l+1])l++;        // 左右两孩子中选择较大者,即mHeap[l+1]if(tmp >= mHeap[l])break;        //调整结束else{mHeap[c] = mHeap[l];c = l;l = 2*l + 1;   }       }   mHeap[c] = tmp;
}

接下来实现删除操作:

/*
删除最大堆中的data
返回值:0,成功-1,失败*/
template <typename T>
int MaxHeap<T>::remove(T data)
{int index;// 如果"堆"已空,则返回-1if(mSize == 0)return -1;// 获取data在数组中的索引index = getIndex(data); if (index==-1)return -1;mHeap[index] = mHeap[--mSize];    // 用最后元素填补filterdown(index, mSize-1);        // 从index位置开始自上向下调整为最大堆return 0;
}

下面为整个完整的最大堆的实现:

#include <iomanip>
#include <iostream>
using namespace std;template <typename T>
class MaxHeap
{private:T *mHeap;         // 数据int mCapacity;    // 总的容量int mSize;        // 实际容量// 最大堆的向下调整算法void filterdown(int start, int end);// 最大堆的向上调整算法(从start开始向上直到0,调整堆)void filterup(int start);public:MaxHeap();MaxHeap(int capacity);~MaxHeap();// 返回data在二叉堆中的索引int getIndex(T data);// 删除最大堆中的dataint remove(T data);// 将data插入到二叉堆中int insert(T data);// 打印二叉堆void print();
};/* * 构造函数*/
template <typename T>
MaxHeap<T>::MaxHeap()
{new (this)MaxHeap(30);
}template <typename T>
MaxHeap<T>::MaxHeap(int capacity)
{mSize = 0;mCapacity = capacity;mHeap = new T[mCapacity];
}
/* * 析构函数*/
template <typename T>
MaxHeap<T>::~MaxHeap()
{mSize = 0;mCapacity = 0;delete[] mHeap;
}/* * 返回data在二叉堆中的索引** 返回值:*     存在 -- 返回data在数组中的索引*     不存在 -- -1*/
template <typename T>
int MaxHeap<T>::getIndex(T data)
{for(int i=0; i<mSize; i++)if (data==mHeap[i])return i;return -1;
}/* * 最大堆的向下调整算法** 注:数组实现的堆中,第N个节点的左孩子的索引值是(2N+1),右孩子的索引是(2N+2)。** 参数说明:*     start -- 被下调节点的起始位置(一般为0,表示从第1个开始)*     end   -- 截至范围(一般为数组中最后一个元素的索引)*/
template <typename T>
void MaxHeap<T>::filterdown(int start, int end)
{int c = start;             // 当前(current)节点的位置int l = 2*c + 1;           // 左(left)孩子的位置T tmp = mHeap[c];         // 当前(current)节点的大小while(l <= end){// "l"是左孩子,"l+1"是右孩子if(l < end && mHeap[l] < mHeap[l+1])l++;        // 左右两孩子中选择较大者,即mHeap[l+1]if(tmp >= mHeap[l])break;        //调整结束else{mHeap[c] = mHeap[l];c = l;l = 2*l + 1;   }       }   mHeap[c] = tmp;
}/** 删除最大堆中的data** 返回值:*      0,成功*     -1,失败*/
template <typename T>
int MaxHeap<T>::remove(T data)
{int index;// 如果"堆"已空,则返回-1if(mSize == 0)return -1;// 获取data在数组中的索引index = getIndex(data); if (index==-1)return -1;mHeap[index] = mHeap[--mSize];    // 用最后元素填补filterdown(index, mSize-1);        // 从index位置开始自上向下调整为最大堆return 0;
}/** 最大堆的向上调整算法(从start开始向上直到0,调整堆)** 注:数组实现的堆中,第N个节点的左孩子的索引值是(2N+1),右孩子的索引是(2N+2)。** 参数说明:*     start -- 被上调节点的起始位置(一般为数组中最后一个元素的索引)*/
template <typename T>
void MaxHeap<T>::filterup(int start)
{int c = start;            // 当前节点(current)的位置int p = (c-1)/2;        // 父(parent)结点的位置 T tmp = mHeap[c];        // 当前节点(current)的大小while(c > 0){if(mHeap[p] >= tmp)break;else{mHeap[c] = mHeap[p];c = p;p = (p-1)/2;   }       }mHeap[c] = tmp;
}/* * 将data插入到二叉堆中** 返回值:*     0,表示成功*    -1,表示失败*/
template <typename T>
int MaxHeap<T>::insert(T data)
{// 如果"堆"已满,则返回if(mSize == mCapacity)return -1;mHeap[mSize] = data;         // 将"数组"插在表尾filterup(mSize);             // 向上调整堆mSize++;                     // 堆的实际容量+1return 0;
}/* * 打印二叉堆** 返回值:*     0,表示成功*    -1,表示失败*/
template <typename T>
void MaxHeap<T>::print()
{for (int i=0; i<mSize; i++)cout << mHeap[i] << " ";
}int main()
{int a[] = {10, 40, 30, 60, 90, 70, 20, 50, 80};int i, len=(sizeof(a)) / (sizeof(a[0])) ;MaxHeap<int>* tree=new MaxHeap<int>();cout << "== 依次添加: ";for(i=0; i<len; i++){cout << a[i] <<" ";tree->insert(a[i]);}cout << "\n== 最 大 堆: ";tree->print();i=85;tree->insert(i);cout << "\n== 添加元素: " << i;cout << "\n== 最 大 堆: ";tree->print();i=90;tree->remove(i);cout << "\n== 删除元素: " << i;cout << "\n== 最 大 堆: ";tree->print();cout << endl; return 0;
}

下面为二叉堆的最小堆实现:

#include <iomanip>
#include <iostream>
using namespace std;template <typename T>
class MinHeap{private:T *mHeap;        // 数据int mCapacity;    // 总的容量int mSize;        // 实际容量// 最小堆的向下调整算法void filterdown(int start, int end);// 最小堆的向上调整算法(从start开始向上直到0,调整堆)void filterup(int start);public:MinHeap();MinHeap(int capacity);~MinHeap();// 返回data在二叉堆中的索引int getIndex(T data);// 删除最小堆中的dataint remove(T data);// 将data插入到二叉堆中int insert(T data);// 打印二叉堆void print();
};/* * 构造函数*/
template <typename T>
MinHeap<T>::MinHeap()
{new (this)MinHeap(30);
}template <typename T>
MinHeap<T>::MinHeap(int capacity)
{mSize = 0;mCapacity = capacity;mHeap = new T[mCapacity];
}
/* * 析构函数*/
template <typename T>
MinHeap<T>::~MinHeap()
{mSize = 0;mCapacity = 0;delete[] mHeap;
}/* * 返回data在二叉堆中的索引** 返回值:*     存在 -- 返回data在数组中的索引*     不存在 -- -1*/
template <typename T>
int MinHeap<T>::getIndex(T data)
{for(int i=0; i<mSize; i++)if (data==mHeap[i])return i;return -1;
}/* * 最小堆的向下调整算法** 注:数组实现的堆中,第N个节点的左孩子的索引值是(2N+1),右孩子的索引是(2N+2)。** 参数说明:*     start -- 被下调节点的起始位置(一般为0,表示从第1个开始)*     end   -- 截至范围(一般为数组中最后一个元素的索引)*/
template <typename T>
void MinHeap<T>::filterdown(int start, int end)
{int c = start;          // 当前(current)节点的位置int l = 2*c + 1;     // 左(left)孩子的位置T tmp = mHeap[c];    // 当前(current)节点的大小while(l <= end){// "l"是左孩子,"l+1"是右孩子if(l < end && mHeap[l] > mHeap[l+1])l++;        // 左右两孩子中选择较小者,即mHeap[l+1]if(tmp <= mHeap[l])break;        //调整结束else{mHeap[c] = mHeap[l];c = l;l = 2*l + 1;   }       }   mHeap[c] = tmp;
}/** 删除最小堆中的data** 返回值:*      0,成功*     -1,失败*/
template <typename T>
int MinHeap<T>::remove(T data)
{int index;// 如果"堆"已空,则返回-1if(mSize == 0)return -1;// 获取data在数组中的索引index = getIndex(data); if (index==-1)return -1;mHeap[index] = mHeap[--mSize];        // 用最后元素填补filterdown(index, mSize-1);    // 从index号位置开始自上向下调整为最小堆return 0;
}/** 最小堆的向上调整算法(从start开始向上直到0,调整堆)** 注:数组实现的堆中,第N个节点的左孩子的索引值是(2N+1),右孩子的索引是(2N+2)。** 参数说明:*     start -- 被上调节点的起始位置(一般为数组中最后一个元素的索引)*/
template <typename T>
void MinHeap<T>::filterup(int start)
{int c = start;            // 当前节点(current)的位置int p = (c-1)/2;        // 父(parent)结点的位置 T tmp = mHeap[c];        // 当前节点(current)的大小while(c > 0){if(mHeap[p] <= tmp)break;else{mHeap[c] = mHeap[p];c = p;p = (p-1)/2;   }       }mHeap[c] = tmp;
}/* * 将data插入到二叉堆中** 返回值:*     0,表示成功*    -1,表示失败*/
template <typename T>
int MinHeap<T>::insert(T data)
{// 如果"堆"已满,则返回if(mSize == mCapacity)return -1;mHeap[mSize] = data;        // 将"数组"插在表尾filterup(mSize);            // 向上调整堆mSize++;                    // 堆的实际容量+1return 0;
}/* * 打印二叉堆** 返回值:*     0,表示成功*    -1,表示失败*/
template <typename T>
void MinHeap<T>::print()
{for (int i=0; i<mSize; i++)cout << mHeap[i] << " ";
}int main()
{int a[] = {80, 40, 30, 60, 90, 70, 10, 50, 20};int i, len=(sizeof(a)) / (sizeof(a[0])) ;MinHeap<int>* tree=new MinHeap<int>();cout << "== 依次添加: ";for(i=0; i<len; i++){cout << a[i] <<" ";tree->insert(a[i]);}cout << "\n== 最 小 堆: ";tree->print();i=15;tree->insert(i);cout << "\n== 添加元素: " << i;cout << "\n== 最 小 堆: ";tree->print();i=10;tree->remove(i);cout << "\n== 删除元素: " << i;cout << "\n== 最 小 堆: ";tree->print();cout << endl; return 0;
}

测试程序已经包含在相应的实现文件(MaxHeap.cpp)中了,下面只列出程序运行结果。

最大堆(MaxHeap.cpp)的运行结果:
== 依次添加: 10 40 30 60 90 70 20 50 80
== 最 大 堆: 90 80 70 60 40 30 20 10 50
== 添加元素: 85
== 最 大 堆: 90 85 70 60 80 30 20 10 50 40
== 删除元素: 90
== 最 大 堆: 85 80 70 60 40 30 20 10 50

二叉堆(c++实现)相关推荐

  1. 大根堆的删除c语言,二叉堆(一)之 C语言详解

    本文介绍二叉堆,二叉堆就是通常我们所说的数据结构"堆"中的一种.和以往一样,本文会先对二叉堆的理论知识进行简单介绍,然后给出C语言的实现.后续再分别给出C++和Java版本的实现: ...

  2. 在A*寻路中使用二叉堆

    在A*寻路中使用二叉堆 作者:Patrick Lester(2003年4月11日更新) 译者:Panic 2005年3月28日 译者序:     这一篇文章,是"A* Pathfinding ...

  3. 0x17.基础数据结构 - 二叉堆

    目录 一.二叉堆 二.例题 0.AcWing 145. 超市 AcWing 146. 序列(POJ 2442) 三.HuffmanHuffmanHuffman树 1.AcWing 148. 合并果子 ...

  4. 大顶堆删除最大值_算法学习笔记(47): 二叉堆

    堆(Heap)是一类数据结构,它们拥有树状结构,且能够保证父节点比子节点大(或小).当根节点保存堆中最大值时,称为大根堆:反之,则称为小根堆. 二叉堆(Binary Heap)是最简单.常用的堆,是一 ...

  5. 排序算法之——优先队列经典实现(基于二叉堆)

    许多应用都需要处理有序的元素,但有时,我们不要求所有元素都有序,或是一定要一次就将它们排序,许多情况下,我们会收集这些元素里的最大值或最小值. 这种情况下一个合适的数据结构应该支持两种操作:插入元素. ...

  6. 线段树、二叉堆以及离散化入门

    目录 线段树 例题 题面 练习 1 2 3 4 5 小解区间操作 二叉堆 例题 思路 @ 线段树 例题 题面 时间限制: 1 Sec 内存限制: 128 MB [题意]给出N个数,两种操作:1.C x ...

  7. 结构之美——优先队列基本结构(四)——二叉堆、d堆、左式堆

    实现优先队列结构主要是通过堆完成,主要有:二叉堆.d堆.左式堆.斜堆.二项堆.斐波那契堆.pairing 堆等. 1. 二叉堆 1.1. 定义 完全二叉树,根最小. 存储时使用层序. 1.2. 操作 ...

  8. 二叉堆的优先队列基本原理及实现

    原理: 传统的队列是先进先出的数据结构,队列的重要变种称为优先级队列 二叉堆常见的遍体:最小堆(其中最小的键在前面)和最大堆(其中最大的键值总是在前面) 代码实现

  9. 2021-10-21 二叉堆 恋上数据结构笔记

    文章目录 引言:Top K问题 堆 二叉堆 二叉堆的实现原理 二叉堆的添加原理 二叉堆的删除 批量建堆(自上而下的上滤与自下而上的下滤)及其效率对比 引言:Top K问题 堆 二叉堆 二叉堆的实现原理 ...

  10. java 二叉堆_二叉堆(三)之 Java的实现

    概要 前面分别通过C和C++实现了二叉堆,本章给出二叉堆的Java版本.还是那句话,它们的原理一样,择其一了解即可. 二叉堆的介绍 二叉堆是完全二元树或者是近似完全二元树,按照数据的排列方式可以分为两 ...

最新文章

  1. MapReduce的数据去重功能
  2. css设置标题边框,css 如何让文字标题显示在边框上?
  3. Spring Boot WebFlux-02——WebFlux Web CRUD 实践
  4. leetcode 岛屿的个数
  5. mysql 重置密码模式_mysql--重置密码
  6. 初级程序员需要接触好的架构代码
  7. 如何:将项添加到缓存中
  8. [转]如何撰写学术论文
  9. python勒索病毒,新型勒索病毒Crypted强势来袭
  10. 我的Java秋招面经大合集
  11. 智能手机屏幕尺寸和分辨率一览表
  12. 为报复老东家,程序员编码给自己转账553笔,金额超21万元
  13. mysql 两阶段加锁_MySQL的两阶段加锁协议
  14. 【五分钟力扣】198题—用python3解决打家劫舍问题
  15. CDH/HDP迁移之路
  16. 【不务正业】减肥、想瘦,我在csdn学减肥之利用python帮助女友减脂
  17. 一个精美的登录界面原来是这样做的
  18. oracle面试题答案,Oracle面试题笔试题及参考答案
  19. 程序员的奋斗史(二十八)——寒门再难出贵子?
  20. 需求文档撰写——心得

热门文章

  1. iOS调用系统相册显示英文
  2. ppt制作教程(3)
  3. 深入浅出了解Struts的处理流程(有图有真相)
  4. 专业人才为贵阳大数据建言献计
  5. 【MySQL】InnoDB行格式、数据页结构以及索引底层原理分析
  6. 计算机视觉:从数据量、数据质量、数据复杂度、数据隐私介绍图片数据处理难度
  7. Linux中redis主从配置
  8. 活动复盘:从策划发起到执行收尾,一场万人参加的活动该如何策划?
  9. Vue实现Element UI的下拉框默认选中,值来自父组件或异步获取
  10. Python调用OpenStack API 《通过RESTful编写Python运维》