一.首先说一下堆的概念吧这里就不按照标准的概念来说了,就说说我理解的堆。

堆就是一个数组中的元素,参考着完全二叉树的这种数据结构存储在数组中,这样就是一个堆。注意:这里是参考,实际的存储还是在数组中,只不过数组中的存储顺序满足完全二叉树而已。说到堆离不开大小堆,下面继续介绍大小堆的概念。

1.最小堆:因为数组中的元素是参考完全二叉树的存储顺序存储的,所以小堆就是每一个双亲结点的值都<=左右孩子的值,堆顶的元素是最小值

2.最大堆:任何一个双亲结点的值都大于等于左右孩子的值,并且堆顶元素的值是最大值。

二.下面来说说如何创建一个堆,因为堆离不开最大堆和最小堆,所以我这里创建的堆是生成了自动生成了最小堆,首先给出代码,然后在给出代码解释:

#include<iostream>
#include<vector>
using namespace std;
template<class T>
class Heap//堆
{
public:Heap(T* array, size_t size)//构造函数:_size(size){_array.resize(size);for (size_t i = 0; i < _size; i++){_array[i] = array[i];}_AdjustDown((_size - 2) >> 1);}T top() const//返回堆顶的元素{T a = _top();return a;}void Push(const T& data)//给堆中插入元素,使用向上调整插入{_Push(data);}void Pop()//删除堆中的元素其实就是删除堆顶的元素{_Pop();}
private:void _AdjustDown(int parent)//从上往下调整,建立小堆{for (int i = parent; i >= 0; i--){parent = i;size_t child = parent * 2 + 1;while (child<_size){if (child + 1 < _size&&_array[child] > _array[child + 1])child += 1;if (/*child<_size&&*/_array[parent] > _array[child]){swap(_array[parent], _array[child]);parent = child;child = parent * 2 + 1;}else{break;}}}}void _AdjustUp(size_t child)//向上调整,插入中使用{size_t parent = (child - 1) >> 1;while (child > 0){if (_array[parent] > _array[child]){swap(_array[parent], _array[child]);child = parent;parent = (child - 1) >> 1;}elsebreak;}}T _top() const{return _array[0];}void _Push(const T data)//堆中插入元素{_array.push_back(data);_size++;if (_array.size() > 1){_AdjustUp(_size - 1);}}void _Pop()//删除堆顶元素{if (_array.empty())return;size_t last = _size - 1;swap(_array[0], _array[last]);_array.pop_back();_size--;if (_size > 1){_AdjustUp(0);}}
private:vector<T> _array;size_t _size;
};
int main()
{int array[] = { 53, 17, 78, 9, 45, 65, 87, 23 };Heap<int> s(array, sizeof(array) / sizeof(*array));s.Push(1);s.Pop();cout << s.top() << endl;system("pause");return 0;
}

上面是整体所有的代码下面给出解析:

1.创建堆(自动生成最小堆)

Heap(T* array, size_t size)//构造函数:_size(size){_array.resize(size);for (size_t i = 0; i < _size; i++){_array[i] = array[i];}_AdjustDown((_size - 2) >> 1);}

这段代码就不需要我多说了吧,将数组中的元素保存在容器vector中,下面我来终点介绍这段代码:即得出最小堆的方法--->向下调整法

_AdjustDown((_size - 2) >> 1);

注意想要生成最小堆的方法是向下调整法,并不是说要生成最大堆就用向上调整法,想要生成最大堆,只需要将其中比较的顺序符号反过来就可以了。而向上调整法是给堆中插入元素时使用的。

2.下面我来说说向上调整法

3.好了关于堆最核心的两个算法已经讲完了,剩下的就是一下很简单的东西了,下面我来说说关于堆的删除即Pop()

void _Pop()//删除堆顶元素{if (_array.empty())return;size_t last = _size - 1;swap(_array[0], _array[last]);_array.pop_back();_size--;if (_size > 1){_AdjustUp(0);}}

算法核心就是将堆顶元素和最后一个元素互换,然后剔除互换后的最后一个元素(即要删除的元素),在重新调整堆就好了。

到这里关于堆的一些问题就讲完了置于剩下的那几个功能都很简单自己去看就可以了。测试代码我在最开是的代码中也已经给出了自己去测试就可以了。

虽然上面 的堆已经说完了但是美中不足的是,每次我创建的总是最小堆,要是我想创建最大难道每次都要去将其中的符号改动吗?当然不需要,下面我就来说说升级版本的,这个版本中加入了仿函数,使一切看起就变得很简单了

1.首先说一下什么是仿函数

其实就是在你定义的类中重载(),实现一个具体的功能,然后这个类对象就可以当成函数调用了!

举个简单的例子:

template<class T>
class Add//定义具有加法功能的仿函数
{
public:int operator()(const T& a, const T& b){int  sum;sum = a + b;return sum;}
};
template<class T>
class Mul//定义具有乘法功能的仿函数
{
public:int operator()(const T& a, const T& b){int  sum;sum = a*b;return sum;}
};
template<class T,class CA>//在这里只是多了个参数而已
class Caculate
{
private:int a;int b;
public:Caculate(int x, int y):a(x), b(y){}CA s;//仿函数类的对象int jisuanqi(){return s(a,b);//把对象当成函数调用}
};
int main()
{Caculate<int,Add<int>> s(10, 10);//在这里你向进行什么运算取决于你传过去的是哪个对象cout << s.jisuanqi() << endl;//执行加法运算system("pause");return 0;
}

上面就是一个简单的仿函数的应用。如果有不理解的地方私我。

下面来说说把仿函数应用到堆中的改造方法,因为最大小堆的建立就是取决于如下语句

if (child + 1 < _size&&com(_array[child+1], _array[child]))child += 1;if (child<_size&&com(_array[child],_array[parent])){swap(_array[parent], _array[child]);parent = child;child = parent * 2 + 1;}

所以我们可以将判断的if语句中的表达式交给仿函数去完成,我们在if里面只需要调用仿函数就可以了,所以产生如下的仿函数

template<class T>
class Less//建立小堆的仿函数
{
public:bool operator()(const T& left, const T& right){return left < right;}
};
template<class T>
class Greater//建立大堆的仿函数
{
public:bool operator()(const T& left, const T& right){return left >right;}
};

使用方法如下

下面给出全部代码:

#include<iostream>
#include<vector>
using namespace std;
//1.函数指针可以实现
//2.仿函数
template<class T>
class Less
{
public:bool operator()(const T& left, const T& right){return left < right;}
};
template<class T>
class Greater
{
public:bool operator()(const T& left, const T& right){return left >right;}
};
//这里创建的都是小堆
template<class T,class Compare=Less<int>>
class Heap//堆
{
public:Heap(T* array, size_t size):_size(size){_array.resize(size);for (size_t i = 0; i < _size; i++){_array[i] = array[i];}_AdjustDown((_size - 2) >> 1);}T top() const//返回堆顶的元素{return _array[0];}void Push(const T& data)//给堆中插入元素,使用向上调整插入{//方法1,直接尾插_array.push_back(data);_size++;if (_array.size() > 1){_AdjustUp(_size - 1);}}void Pop()//删除堆中的元素其实,就是删除堆顶的元素{if (_array.empty())return;size_t last = _size - 1;swap(_array[0], _array[last]);_array.pop_back();_size--;if (_size > 1){_AdjustDown(0);}}
private:void _AdjustDown(int parent)//从上往下调整,建立小堆{for (int i = parent; i >= 0; i--){parent = i;size_t child = parent * 2 + 1;while (child<_size){Compare com;if (child + 1 < _size&&com(_array[child+1], _array[child]))child += 1;if (child<_size&&com(_array[child],_array[parent])){swap(_array[parent], _array[child]);parent = child;child = parent * 2 + 1;}else{break;}}}}void _AdjustUp(size_t child)//向上调整,插入中使用{size_t parent = (child - 1) >> 1;while (child>0){if (Compare()(_array[child],_array[parent])){swap(_array[parent], _array[child]);child = parent;parent = (child - 1) >> 1;}elsebreak;}}
private:vector<T> _array;size_t _size;//缺点就是每次插入的需要调整
};
int main()
{int array[] = { 53, 17, 78, 9, 45, 65, 87, 23 };Heap<int,Greater<int>> s(array, sizeof(array) / sizeof(*array));/*s._downtoup();*/s.Push(1);s.Pop();cout << s.top() << endl;system("pause");return 0;
}

因为测试代码我也给出了,所以自行去测试。

关于堆的内容说完了,下面说说堆的应用

1.优先级队列

template<class T,class Compare>
class Priority
{
public:Priority(){}void Push(const T& data){_ph.Push(data);}void Pop(){_ph.Pop();}T Top() const{return _hp.Top();}bool Empty()const {return _hp.Empty();}
private:Heap<int, Compare> _hp;
};

2.对排序(递增排序)

1.首先建立调整最大堆的的函数

2.将你要排序的数组首先调整一次,生成堆

3.开始排序(交换堆顶和最后一个元素的位置,然后除去最后一个元素,继续调整,一次类推直到全部排序完)

下面给出代码

void Adjustheap(int *array, int size, int parent)//调整堆。调整出来的是大堆
{int child = parent * 2 + 1;while (child<size){if (child+1<size&&array[child]<array[child + 1])child += 1;if (array[parent] < array[child]){swap(array[parent], array[child]);parent = child;child = parent * 2 + 1;}elsereturn;//不需要调整直接结束}
}
void HeapSort(int *array, int size)
{//创建堆int root = (size - 2) >> 1;for (; root > 0; --root){Adjustheap(array, size, root);//调整堆}//堆排序for (int i = 0; i < size-1;i++){swap(array[0],array[size - i - 1]);Adjustheap(array, size - i-1, 0);}
}
int main()
{int array[] = { 5, 3, 2, 4, 1 };HeapSort(array, 5);system("pause");return 0;
}

排序后的数组

3.Topk问题:

即在海量的数据中查找最大或者最小的前 N个数据

1.假如要找前N个最大的数话,首先建立一个元素为N的小堆,然后开始遍历数据,如果数据比堆根大则将堆根pop出去,然后将此元素push进堆,然后继续调整,调整好以后的堆顶是最小的,以此类推,当数据全部扫描完成,则这个堆中的元素就是我们要的数据

2.假如要找前N个最小的数的话,使用最大堆,比堆顶元素小的话pop堆顶,push元素,然后调整堆,直到数据遍历完毕。

以上关于堆的一些内容就介绍完了,希望对大家对于堆的理解有帮助,不懂或出错的地方欢迎留言一起交流学习!

大小堆堆排序堆的应用相关推荐

  1. 看懂堆排序——堆与堆排序(三)

    看懂堆排序--堆与堆排序(三) 看懂堆排序--堆与堆排序(三) 堆排序的基本思想 代码详解 父亲下标和孩子下标的关系 打印数组的函数 下滤函数 构造堆的函数 删除最大元函数 排序主函数 完整代码及运行 ...

  2. golang 排序_堆 堆排序 优先队列 图文详解(Golang实现)

    引入 在实际应用中,我们经常需要从一组对象中查找 最大值 或 最小值 .当然我们可以每次都先排序,然后再进行查找,但是这种做法效率很低.哪么有没有一种特殊的数据结构,可以高效率的实现我们的需求呢,答案 ...

  3. JVM学习笔记之-堆,年轻代与老年代,对象分配过程,Minor GC、Major GC、Full GC,堆内存大小与OOM,堆空间分代,内存分配策略,对象分配内存,小结堆空间,逃逸分析,常用调优工具

    堆的核心概述 概述 一个JVM实例只存在一个堆内存,堆也是Java内存管理的核心区域.Java堆区在JVM 启动的时候即被创建,其空间大小也就确定了.是JVM管理的最大一块内存空间. 堆内存的大小是可 ...

  4. bat java 指定堆大小_jvm 堆内存 栈内存 大小设置 查看堆大小

    1.在eclipse设置JVM参数 打开eclipse-窗口-首选项-Java-已安装的JRE(对在当前开发环境中运行的java程序皆生效,也就是在eclipse中运行的java程序)编辑当前使用的J ...

  5. 堆排序小根堆 大根堆 迭代 递归 总结 完整代码

    http://blog.csdn.net/morewindows/article/details/6709644/ 1.堆基础 堆:完全二叉树或者是近似完全二叉树  大根堆:每个结点的值都大于或等于其 ...

  6. c语言标准模板小顶堆,堆排序(大顶堆、小顶堆)----C语言

    堆排序 之前的随笔写了栈(顺序栈.链式栈).队列(循环队列.链式队列).链表.二叉树,这次随笔来写堆 1.什么是堆? 堆是一种非线性结构,(本篇随笔主要分析堆的数组实现)可以把堆看作一个数组,也可以被 ...

  7. linux 堆地址,堆与堆排序_Linux编程_Linux公社-Linux系统门户网站

    堆排序与 二叉堆的定义 二叉堆是完全二叉树或者是近似完全二叉树. 二叉堆满足二个特性: 1.父结点的键值总是大于或等于(小于或等于)任何一个子节点的键值. 2.每个结点的左子树和右子树都是一个二叉堆( ...

  8. java堆与非堆的一些研究_堆和堆傻傻分不清?一文告诉你 Java 集合中「堆」的最佳打开方式...

    什么是堆? 堆其实就是一种特殊的队列--优先队列. 普通的队列游戏规则很简单:就是先进先出:但这种优先队列搞特殊,不是按照进队列的时间顺序,而是按照每个元素的优先级来比拼,优先级高的在堆顶. 这也很容 ...

  9. 堆和堆傻傻分不清?一文告诉你 Java 集合中「堆」的最佳打开方式

    上一篇的 「Java 集合框架」里,还剩下一个大问题没有说的,那就是 PriorityQueue,优先队列,也就是堆,Heap. 什么是堆? 堆其实就是一种特殊的队列--优先队列. 普通的队列游戏规则 ...

最新文章

  1. gradle入门,安卓gradle入门
  2. 【C++】C++中的头文件(.h)—详解(2)
  3. linux的多任务 多进程,浅谈linux模拟多线程崩溃和多进程崩溃
  4. 如何计算机操作维护培训,电脑基本操作培训教材.ppt
  5. python︱写markdown一样写网页,代码快速生成web工具:streamlit lay-out布局(四)
  6. mysql资源估算_关于数据库查询要耗费的服务器资源估算!高手进~
  7. php表决器代码,三人表决器:VHDL源代码
  8. 带你玩转Visual Studio——带你跳出坑爹的Runtime Library坑
  9. 计算机硬盘根目录是什么,根目录是什么(d盘根目录是什么意思)
  10. 利用边长计算三角形面积 — 海伦公式推导过程
  11. ElasticSearch:简单介绍以及使用Docker部署ElasticSearch 和 Kibana
  12. FaceNet 读书笔记
  13. mybatis 父子级树形结构查询
  14. Java 创建pdf
  15. 计算机开关键是什么符号,按钮开关符号知识大盘点 【图文】
  16. Web前端面试指导(四十):CSS3有哪些新特性?
  17. 浅谈php国际(I18N)以及config包的使用
  18. 解决chunk-vendors包过大问题,利用SplitChunks插件,分离chunk
  19. 关于UniApp启动到微信小程序工具提示找不到app.json
  20. iptables匹配iprange

热门文章

  1. @如何删除重复的行?@
  2. 学霸养成系统APP(app设计)
  3. R语言开发环境搭建与实践
  4. android ios 录音功能,盘点:简单好用的录音APP有哪些?
  5. Unity Shader - 实现类似镜面反射、水面扰动效果
  6. win10 安装.net3.5失败,错误代码0x800f0922
  7. Vivado将程序固化到Xilinx的FPGA开发板的flash芯片中
  8. 系统清理工具:iBoostUp for Mac
  9. ios,android,塞班,iOS塞班给力 非Android热门手机搜罗
  10. Android软件脱壳分析,使用FRIDA为Android应用进行脱壳的操作指南