概述

与FIFO的普通队列不同,在优先级队列中,元素出队顺序是由元素的优先级决定。比如大根堆是最大的元素先出队,小根堆是最小的元素先出队。
堆是实现优先级队列效率很高的数据结构(当然我们也可以使用无序的线性表或者有序链表来实现,但是它们的删除、插入的时间复杂度至少有一个是O(n))。堆是一棵完全二叉树,这样就保证了底层存储的数组拥有很高的空间利用率。无论是大根堆还是小根堆,它们的插入和删除的复杂性都是O(height of tree)也就是O(log n)。
堆的底层实现是数组,但是逻辑上我们将它映射到一棵完全二叉树上。它的插入操作是从数组的末尾的下一个位置也就是完全二叉树的下一个新增的叶子节点开始,逐层遍历它的父节点、祖父节点…直到找到合适的插入位置或者达到根部。反之,它的删除操作则是首先将根节点也就是数组的[0]元素出队,然后从上到下逐层选择合适左右孩子进行遍历。

大根堆的实现

其他例如优先队列ADT或者堆空的自定义异常类等一些辅助类这里就省略了,它们与以前其他数据结构中的实现大同小异 。

#pragma once
#ifndef MAXHEAP_H
#define MAXHEAP_H#include "emptyHeap.h"
#include "priorityQueueADT.h"
//堆空异常 借用了队列的异常类
#include "..//..//..///ch09/Queue/Queue/queueEmpty.h"
#include "..//..//..//ch06/ChainList/ChainList/illegalParameterValue.h"//===================================最大堆的定义实现======================================
template <typename T>
class maxHeap :public priorityQueue<T> {public:maxHeap(size_t _capacity=10);//默认构造函数~maxHeap() { delete[] heap; }bool empty()const { return heapSize == 0; }size_t size()const { return heapSize; }T& top()const;void pop();void push(const T& _element);void heapInitial(T* arr,size_t arrSize);//将给定数组转为最大堆void output(std::ostream& os)const;
private:T* heap;         //优先级队列(堆)数组size_t capcacity;//堆的容量size_t heapSize; //堆的元素个数
};template <typename T>
inline maxHeap<T>::maxHeap(size_t _capacity) {if (_capacity <= 1)throw illegalParameterValue("initial capacity must be >=1 ");capcacity = _capacity + 1;//因为heap[0]不用heap = new T[_capacity];heapSize = 0;
}template <typename T>
inline T& maxHeap<T>::top()const {if (heapSize == 0)throw emptyHeap("Can not get top element on empty Heap!");return heap[1];
}template <typename T>
void maxHeap<T>::push(const T& _element) {if (heapSize == capcacity - 1){//除开heap[0] 数组容量已满T* new_heap = new T[2 * capcacity];std::copy(heap, heap+capcacity+1, new_heap);delete[] heap;heap = new_heap;}size_t pos = ++heapSize;//要插入的位置//在完全二叉树中多出一个位置为++heapSize的节点,从要插入的元素_element的父节点//也就是heap[pos/2]开始逐级向上比较,直到找到要插入的元素在大根堆中的位置(大于子节点且小于父节点)while (pos != 1 && heap[pos/2]<_element) {//最大的话则插入根节点heap[pos] = heap[pos / 2];pos /= 2;//继续向上层比较}//比较完成 插入该元素heap[pos] = _element;
}template <typename T>
void maxHeap<T>::pop() {if (heapSize == 0)throw emptyHeap("Can not pop on empty Heap!");//首先删除根节点也就是heap[1]的元素 然后逐级遍历选取左右孩子中最大的一个移动到该层级来//最后将完全二叉树中最末尾的元素也即是heap[heapSize]的元素移动到空缺的位置 最后删除末尾节点heap[1].~T();//删除根节点元素T lastElement = heap[heapSize--];//重新把最末尾的元素放到合适的位置 同时重构堆int pos = 1;//要插入的位置int child = 2;//它的孩子while (child<=heapSize) {//是左孩子大还是右孩子大?if (child < heapSize && heap[child] < heap[child + 1])++child;//选择右孩子作为下一个pos//可以在这层插入吗?if (lastElement > heap[child])break;//找到位置//否则将大孩子放入pos 移动到下层进行比较heap[pos] = heap[child];pos = child;child *= 2;}//找到位置 插入heap[pos] = lastElement;
}template <typename T>
void maxHeap<T>::heapInitial(T* arr,size_t arrSize) {//将给定数组转为堆delete[] heap;//首先释放原有堆heapSize = arrSize;capcacity = 2*arrSize;//构造一个新的堆数组 heap = new T[capcacity + 1];std::copy(arr,arr+arrSize,heap+1); //heap[0]需要空出来//初始化为堆for (int root = heapSize / 2; root > 0;--root) {//从倒数第二层依次遍历到heap[1]进行调整int child = 2 * root;//其孩子节点T rootElement = heap[root];//从上到下依次调整while (child<=heapSize) {//找到左右孩子中大的一个if (child < heapSize && heap[child] < heap[child + 1])++child;//右孩子大//当前位置是否可以插入if (heap[child] < rootElement)break;//可以插入 跳出//不可以插入 调整并且继续移动到下层heap[child / 2] = heap[child];child *= 2;}//找到合适的插入位置heap[child / 2] = rootElement;}
}template <typename T>
void maxHeap<T>::output(std::ostream& os)const {for (size_t i = 1; i <= heapSize;++i) {os << heap[i] << ends;}os << endl;
}template <typename T>
std::ostream& operator<<(std::ostream& os, const maxHeap<T>& heap) {heap.output(os);return os;
}#endif // !MAXHEAP_H

小根堆的实现

小根堆的实现和大根堆的实现一模一样,只是在插入或者删除以及堆化一个给定数组的时候,对类型T的判断条件相反。即大根堆如果元素大于其父节点则将父节点下移,而小根堆相反,如果元素小于其父节点则将父节点下移。

#pragma once
#ifndef MINHEAP_H
#define MINHEAP_H//小根堆跟大根堆的实现一模一样 只是调整树结构的时候是将小的放在根节点
//大的元素放在左右节点#include "priorityQueueADT.h"
#include "emptyHeap.h"
#include "..//..//..///ch09/Queue/Queue/queueEmpty.h"//===================================最小堆的定义实现======================================
template <typename T>
class minHeap :public priorityQueue<T> {public:minHeap(size_t _capacity = 10) :capacity(_capacity+1),heapSize(0),heap(new T[capacity+1]){ }~minHeap() { delete[] heap; }bool empty()const { return heapSize == 0; }size_t size()const { return heapSize; }T& top()const {if (heapSize == 0)throw emptyHeap("Can not get top element on emtpy heap");return heap[1];}void push(const T& _element);void pop();void heapInitial(T* arr, size_t arrSize);//将给定数组转为最小堆void output(std::ostream& os)const;
private:size_t capacity;size_t heapSize;T* heap;
};//插入操作是从下向上依次调整
template <typename T>
void minHeap<T>::push(const T& _element) {if (heapSize == capacity - 1) {T* new_heap = new T [2 * capacity];std::copy(heap, heap + capacity+1, new_heap);delete heap;heap = new_heap;}//依次向上调整int pos = ++heapSize;while (pos>=1 && _element < heap[pos / 2]) {heap[pos] = heap[pos / 2];pos /= 2;}heap[pos] = _element;
}//pop操作则是从上到下一次调整
template <typename T>
void minHeap<T>::pop() {if (heapSize <= 0)throw emptyHeap("Can not pop on empty heap");heap[1].~T();//删除并析构堆顶元素T lastElement = heap[heapSize--];//保存最后的一个元素int child = 2;//从第二层开始向下遍历while (child<=heapSize) {if (child<heapSize && heap[child]>heap[child + 1])++child;//选择左右孩子中最小的那个if (lastElement < heap[child])break;//找到可以插入的位置heap[child / 2] = heap[child];//调整child *= 2;//移动到下层}heap[child / 2] = lastElement;
}//初始化一个数组为最小堆 同样是从上到下进行遍历调整
template <typename T>
void minHeap<T>::heapInitial(T* arr,size_t arrSize) {delete[] heap;//释放原有heap数组heapSize = arrSize;capacity = 2 * arrSize;heap = new T[capacity + 1];std::copy(arr, arr + arrSize, heap + 1);//堆化for (int root = heapSize / 2; root > 0;--root) {T rootElement = heap[root];int child = 2 * root;while (child <= heapSize) {if (child<heapSize && heap[child]>heap[child + 1])++child;//找到左右孩子中最小的那个if (rootElement < heap[child])break;//找到可以插入的位置heap[child / 2] = heap[child];child *= 2;}heap[child / 2] = rootElement;}
}template <typename T>
void minHeap<T>::output(std::ostream& os)const {for (size_t i = 1; i <= heapSize; ++i) {os << heap[i] << ends;}os << endl;
}template <typename T>
std::ostream& operator<<(std::ostream& os, const minHeap<T>& heap) {heap.output(os);return os;
}#endif // !MINHEAP_H

优先级队列--大根堆和小根堆相关推荐

  1. 【数据结构】堆,大根堆,小根堆,优先队列 详解

    目录 堆 1.堆的数组实现 2.小根堆 3.大根堆 4.优先队列 例题 1.SP348 EXPEDI - Expedition(有趣的贪心思路,优先队列) 2.合并果子 堆 要了解堆之前,请先了解树, ...

  2. 堆(大根堆、小根堆)

    完全二叉堆 堆又可称之为完全二叉堆.这是一个逻辑上基于完全二叉树.物理上一般基于线性数据结构(如数组.向量.链表等)的一种数据结构. 完全二叉树的存储结构 学习过完全二叉树的同学们都应该了解,完全二叉 ...

  3. 大根堆和小根堆的区别

    大根堆和小根堆的区别 文章转自:https://blog.csdn.net/weixin_37197708/article/details/79546535 堆的概念 堆实际上是一棵完全二叉树,其任何 ...

  4. 堆结构 - 大根堆、小根堆

    在开发语言中,heap在使用层次的名字叫PriorityQueue(优先级队列),PriorityQueue数据结构的名字就叫做堆,底层就是用堆结构实现的. 完全二叉树 空树也算是完全二叉树 每一层都 ...

  5. 堆(Heap)大根堆、小根堆

    目录 堆(Heap)大根堆.小根堆 1.堆的存储: 2.堆的操作:insert 3.堆的操作:Removemax 4.堆的操作:buildHeap 堆化数组 5.堆排序 堆(Heap)大根堆.小根堆 ...

  6. 【堆】 大根堆和小根堆的建立

    堆是一种经过排序的完全二叉树,其中任一非终端节点的数据值均不大于(或不小于)其左孩子和右孩子节点的值. (1)根结点(亦称为堆顶)的关键字是堆里所有结点关键字中最小者的堆称为小根堆. (1)根结点(亦 ...

  7. C语言实现选择排序——堆排序(大根堆、小根堆)

    C语言实现堆排序 文章目录 C语言实现堆排序 大根堆排序算法 1.交换操作 2.对结点进行调整为大根堆 3.建立大根堆 4.大根堆排序算法实现 小根堆排序算法 1.交换操作 2.对结点进行调整为小根堆 ...

  8. 浅谈大根堆,小根堆,以及堆排序(python)实现

    既然要说堆排序,那么必然要说说什么是大根堆,小根堆了. 大根堆: 若根节点存在左右子节点,那么根节点的值大于或等于左右子节点的值. 小根堆: 若根节点存在左右子节点,那么根节点的值小于或等于左右子节点 ...

  9. 大根堆、小根堆(数组模拟操作)

    1.什么是大根堆.小根堆? 首先堆应该是一颗完全二叉树,大根堆就是二叉树的所有父节点的值都比左右孩子的值大,小根相反.下面是大根堆和小根堆的图: 如上,左图是一个大根堆,右图是一个小根堆. 2.树的数 ...

最新文章

  1. 2022-2028年中国联合办公行业深度调研及投资前景预测报告
  2. ui-router中使用ocLazyLoad和resolve
  3. (2)ARM Cortex-M3指令集
  4. java如何解决高并发问题_java怎么处理高并发?
  5. PERL 实现微信登录
  6. ⑥又是星期五,小试牛刀(编写定制标签)
  7. 大学生换学校学计算机,高校换上新课桌,同学表示“世界观被颠覆”,网友:黑科技的诞生...
  8. anaconda安装sklearn_1. Sklearn —— 简介+安装
  9. python2和3切换_python2和python3切换
  10. HAOI2008 硬币购物
  11. 和浏览器并发请求数有关的一些前端技术
  12. jave double相加结果误差+尾巴
  13. 操作系统排名服务器,服务器操作系统排行榜
  14. 多行文字内容溢出显示点点点(...)省略号
  15. Java必突-JVM知识专题(一): Java代码是如何跑起来的+类加载到使用的过程+类从加载到使用核心阶段(类初始化)+类加载的层级结构+什么是JVM的内存区域划分?Java虚拟机栈、Java堆内存
  16. 3D软件中怎么绘制杯子?
  17. java中不等于空怎么写_JAVA判断不等于空的情况
  18. mysql中的强制索引_你如何强制MySQL中的查询使用索引?
  19. 高考后能学习——阿里云-winserver服务器购买以及使用(包含【.Net】、【PHP】、【MySQL】、【Navicat】、【Java】、安装)
  20. 我30岁了,转行学软件自动化测试可以吗? 排除法告诉你答案

热门文章

  1. Windows下 c++ TCP通信
  2. 【生活百科】感冒用药,止咳祛痰清涕
  3. 2016/10/8 1001. 舞蹈室安排
  4. 【转】探索推荐引擎内部的秘密
  5. C++ 面向对象经典练习魔兽世界装备
  6. 邻居盖房不留滴水的解决办法
  7. ShareSdk申请QQ互联ID、KEY
  8. 安装VMware报错:无法安装服务vmware Authorization server请确保你有足够的权限
  9. 新手小白一部手机搞定,做二次剪辑短视频,无需颜值才艺
  10. android 双sd卡,华为Mate7如何实现双卡双待同时插入SD卡 华为Mate7双卡双待使用教程...