(二叉)堆是一个数组,是一颗近似完全二叉树,分为大顶堆&小顶堆。表示堆的数组A有两个属性:(1)A.length表示数组元素的个数;(2)A.heap-size表示有多少个堆元素存储在数组A中。更多的关于堆的性质的介绍:算法导论第三版:p85-p89、编程珠玑:p141-p145。

堆的操作主要包括堆插入堆删除两个,而堆插入设计到SiftUp操作(自底向上调整),堆删除涉及到SiftDown操作(自顶向下调整,大顶堆时对应算法导论上的MAX-HEAPIFY操作)。

下面是大顶堆和小顶堆的基本操作的C++的实现:

//heap.h
#pragma oncetemplate<class T>
class Heap
{
public:Heap(const int nmax = 100) : max_size(nmax){arr = new T[max_size];size = 0;}virtual ~Heap() {delete []arr;}void Insert(const T &e);   void Delete(const T &e);int Search(const T &e) const;void Print() const;
protected:T *arr;const int max_size;    //max array elementsint size;   //size of heapvirtual void SiftDown(int i) = 0;    //adjust from up to downvirtual void SiftUp(int i) = 0;    //adjust from down to up
};template<class T>
void Heap<T>::Insert(const T &e)
{if (size == max_size)return;arr[size++] = e;  //将新插入的元素添加在最后位置并增加sizeSiftUp(size - 1);  //数组下标从0开始
}template<class T>
void Heap<T>::Delete(const T &e)
{int pos = Search(e);if (pos == -1)return;arr[pos] =    arr[--size];    //用最后的元素填充pos位置的元素,并减少sizeSiftDown(pos);
}template<class T>
int Heap<T>::Search(const T &e)const
{for (int i = 0; i < size; i++)if (arr[i] == e)return i;return -1;
}template<class T>
void Heap<T>::Print()const
{for (int i = 0; i < size; i++)cout << arr[i] << " ";cout << endl;
}

最大堆:maxHeap.h

//maxHeap.h
#pragma once
#include "heap.h"
#include <iostream>
using std::cout;
using std::endl;template<class T>
class MaxHeap : public Heap<T>
{
public:MaxHeap(const int nmax = 100) : Heap(nmax) {}   ~MaxHeap() {}
private:virtual void SiftDown(int i);   //adjust from up to downvirtual void SiftUp(int i); //adjust from down to up
};template<class T>
void MaxHeap<T>::SiftDown(int i)
{   //已知arr[i,...,size)除arr[i]之外均满足大顶堆的定义,本函数向下调整arr[i]//使得在具有size个结点的堆中,以i为下标的根节点的子树重新遵循最大堆的性质//size为节点总数,从0开始计算,i节点的子节点为 2*i+1, 2*i+2int tmp, j;j = 2 * i + 1;//结点i的左孩子下标tmp = arr[i];while (j < size){if (j + 1 < size && arr[j + 1] > arr[j])//找左右孩子中最大的j++;if (arr[j] <= tmp)   // 父结点tmp=arr[i]比孩子结点arr[j]大break;arr[i] = arr[j];//把较大的子结点往上移动,替换它的父结点i = j;j = 2 * i + 1;}arr[i] = tmp;
}
template<class T>
void MaxHeap<T>::SiftUp(int i)
{//新加入结点i,原本arr[0, size)满足堆性质,向上调整使得[0, size+1)满足最大堆性质int j, tmp;j = (i - 1) / 2; //i的父结点tmp = arr[i];while (j >= 0 && i != 0){if (arr[j] >= tmp)    //父节点arr[j]比子结点tmp = arr[i]大break;arr[i] = arr[j];//把较小的父结点往下移动,替换它的子结点i = j;j = (i - 1) / 2;}arr[i] = tmp;
}

最小堆:minHeap.h

//minHeap.h
#pragma once
#include "heap.h"
#include <iostream>
using std::cout;
using std::endl;template<class T>
class MinHeap : public Heap<T>
{
public:MinHeap(const int max_size = 100) : Heap(max_size) {}~MinHeap(){}
private:virtual void SiftDown(int i);   virtual void SiftUp(int i);
};template<class T>
void MinHeap<T>::SiftDown(int i)
{//已知arr[i,...,size)除arr[i]之外均满足小顶堆的定义,本函数向下调整arr[i]//使得在具有size个结点的堆中,以i为下标的根节点的子树重新遵循最大堆的性质//size为节点总数,从0开始计算,i节点的子节点为 2*i+1, 2*i+2int j, tmp;j = 2 * i + 1; //左孩子tmp = arr[i];while (j < size ){if (j + 1 < size && arr[j + 1] < arr[j])   //找左右孩子中最小的j++;if (arr[j] >= tmp) //   父结点tmp=arr[i]比孩子结点arr[j]小break;arr[i] = arr[j];  //把较小的子结点往上移动,替换它的父结点i = j;j = 2 * i + 1;}arr[i] = tmp;
}template<class T>
void MinHeap<T>::SiftUp(int i)
{int j, tmp;j = (i - 1) / 2;  //父结点tmp = arr[i];while (j >= 0 && i != 0){if (arr[j] <= tmp)  //父节点arr[j]比子结点tmp = arr[i]小break;arr[i] = arr[j];   //把较大的父结点往下移动,替换它的子结点i = j;j = (i - 1) / 2;}arr[i] = tmp;
}

测试代码:

#include "maxHeap.h"
#include "minHeap.h"int main()
{int a[12] = {15, 13, 9, 5, 12, 8, 7, 4, 0, 6, 2, 1};MaxHeap<int> maxheap(20);MinHeap<int> minheap(20);for (int i = 0; i < 12; i++)maxheap.Insert(a[i]);  //向上调整maxheap.Print();maxheap.Delete(4);    //向下调整maxheap.Print();for (int i = 0; i < 12; i++)minheap.Insert(a[i]);   //向上调整minheap.Print();minheap.Delete(4);    //向下调整minheap.Print();getchar();return 0;
}

这里用的是插入的方法建堆:最坏情况下,建立一个包含n个元素的堆的时间复杂度是O(nlgn),大theta。

下一篇文中的堆排序,可以在线性时间O(n)内,把一个无需数组构造成一个最大堆

一般地,还要k叉堆:算法导论第三版p93。

利用堆可以实现:堆排序、优先队列。在Dijkstra算法中: EXTRACT-MIN(Q) 操作,就可以先建立最小堆,然后从最小队列中,每次抽取最小结点(参考 最短路径之Dijkstra算法 一文的参考资料链接)。

此外,堆还可以用于:海量数据中选择前k个最大(最小)的数。

思想:在一个很大的无序数组里面选择前k个最大(最小)的数据,最直观的做法是把数组里面的数据全部排好序,然后输出前面最大(最小)的k个数据。但是,排序最好需要O(nlogn)的时间,而且我们不需要前k个最大(最小)的元素是有序的。这个时候我们可以建立k个元素的最小堆(得出前k个最大值)或者最大堆(得到前k个最小值),我们只需要遍历一遍数组,在把元素插入到堆中去只需要logk的时间,这个速度是很乐观的。利用堆得出前k个最大(最小)元素特别适合海量数据的处理。

参考资料:算法导论第三版:p85-p89、编程珠玑:p141-p145

数据结构之(二叉)堆相关推荐

  1. 【数据结构】二叉堆、TOP K 问题

    二叉堆.TOP K 问题 堆(Heap) 堆的出现,思考? 堆简介 二叉堆(Binary Heap) 获取最大值 最大堆 - 添加 最大堆 - 添加优化 最大堆 - 删除 replace 最大堆 – ...

  2. 数据结构:二叉堆原理及实现

    二叉堆 二叉堆数组 最小堆和最大堆 最小堆的实现 创建最小堆及初始化参数 二叉堆数组扩容 数组索引值对应的value交换 上浮 下沉 添加value 获取堆顶最值 任意索引的数据上浮或下沉 二叉堆数组 ...

  3. java 二叉堆_【数据结构】二叉堆:Java实现最大堆及堆排序

    堆在逻辑上一棵完全二叉树,所以可以通过数组进行数据存储,而其余的树大多采用链式结构进行数据存储 堆分类: 大顶堆:大顶堆就是无论在任何一棵(子)树中,父节点都是最大的 小顶堆:小顶堆就是无论在任何一棵 ...

  4. 【数据结构与算法】二叉堆V2.0的Java实现

    更新说明 我们在此前已经编写过简单版的二叉大根堆V1.0,这次,换成二叉小根堆,命名为二叉堆V2.0. 大家也知道,堆是完全二叉树,存储方式借助动态数组实现顺序存储,依赖于父子结点之间的index关系 ...

  5. 【数据结构与算法】二叉堆与二叉搜索树的区别

    问题描述 记得刚学习数据结构的时候,就容易混淆二叉堆和二叉搜索树,其实虽说堆也是一种完全二叉树,但二者差别还是挺大的,本文试做分析. 逻辑结构 二叉堆和二叉搜索树都是结点带权重,并在父子结点间满足某种 ...

  6. 二叉堆--insert操作以及deleteMin操作的实现(C语言)

    二叉堆--insert操作以及deleteMin操作的实现(C语言) 一.概念回顾 1. 什么是二叉堆? 二叉堆其实就是一棵完全二叉树,但是它相比一般的完全二叉树又多了一些限制: (1)对于二叉堆来说 ...

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

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

  8. 数据结构之优先队列--二叉堆(Java实现)

    前言 数据结构队列的学习中,我们知道队列是先进先出的.任务被提交到队列中,按照先进先出的原则 对各个任务进行处理.不过在现实的情况下,任务通常有着优先级的概念,例如短任务.管理员的操作 应该优先执行. ...

  9. 【数据结构与算法】二项队列与二叉堆的比较

    导语 二叉堆确实是入门级的重要数据结构了,而二项队列也是慢慢要去掌握的一种支持高效合并的优先队列实现.本文稍作比较,望抛砖引玉. 列个表格比较基本操作性能 基本操作 insert(平均) delete ...

  10. 《恋上数据结构第1季》二叉堆实现优先级队列

    优先级队列(Priority Queue) 优先级队列简介 优先队列的底层实现 二叉堆实现优先级队列源码 测试代码 数据结构与算法笔记目录:<恋上数据结构> 笔记目录 想加深 Java 基 ...

最新文章

  1. java 字符处理_Java字符串处理实用代码
  2. Hadoop API文档地址
  3. 彻底理解Canal,看这篇就够了
  4. JSjQuery全选反选父项子项联动多选框
  5. Silverlight 设计器加载错误
  6. python读取matlab数据_两分钟搞定Python读取matlab的.mat数据
  7. 警惕!这个 PowerPoint“弱点”可用于发动鼠标悬停攻击
  8. 消息分流器-HANDLE_MSG
  9. 创新彰显实力,方正璞华又获一项国家发明专利
  10. 隔空开车,华为云EI圆我“武侠梦”
  11. 查看进程是否是由于linux OOM killer机制杀死的方法
  12. form表单提交后,2次刷新问题记录
  13. 腾讯的内外社区实践及未来开源布局
  14. spring-boot-maven-plugin:3.0.0:repackage 报错【解决】
  15. 微信屏蔽防封跳转、域名防红系统的实现原理
  16. 【APIO2016】烟火表演
  17. 为什么越来越多的企业选择云计算?—分析云计算的优势和未来发展趋势
  18. 网站打开速度慢的原因,排查方法及优化方法(大全)
  19. U盘启动器安装双系统(Win10+RHEL8.0)过程中的问题总结- 安装源出现设置基础软件仓库时出错、安装目的地中识别不出未分配的空闲空间问题、iso写入U盘做启动器的工具
  20. 【论文分享】GNN+小样本文本分类方法:Meta-GNN: On Few-shot Node Classification in Graph Meta-learning

热门文章

  1. C调用shellcode方法总结
  2. lg显示器工厂模式怎么进入_LG液晶显示器进入工厂模式方法
  3. alertmanager 钉钉告警配置
  4. [10.96.0.1]:443/apis/crd.projectcalico.org/v1/clusterinformations/default: timeout
  5. ZCMU 1411 喜闻乐见的a+b
  6. Unity Shader-热空气扭曲效果(多种实现方案,包括移动平台)
  7. 计算机发展趋势起点,2020年烟台市中考芝罘、莱山、高新、牟平四区结果浅析...
  8. 2022年全国大学生数学建模竞赛E题目-小批量物料生产安排详解+思路+Python代码时序预测模型(三)
  9. ESP Wi-Fi 连接异常断开原因排查分析
  10. 产品创新与研发管理的八大典型问题