堆的基本实现及优先级队列问题
来源:互联网 发布:网络机顶盒破解 编辑:程序博客网 时间:2024/05/23 13:12
堆数据结构是一种数组对象,它可以被视为一棵完全二叉树结构。堆结构的二叉树存储有两种:
最大堆:每个父节点的都大于孩子节点;
最小堆:每个父节点的都小于孩子节点。
下来以大堆为例:
1.如何建堆?
1>首先我们选取vector来对堆中元素进行存储;
2>由于堆数据结构可被视为一棵完全二叉树,我们可以从最后一个非叶子节点起往上调整;但是须得注意,这个首先得满足左右子树都是大堆得情况。
3>上调过程中,先让child指向左孩子,然后进行左,右孩子的比较,选出较大的一个,再与父节点比较,如果父节点比两孩子中较大那个值小,则需交换父节点与子节点,直至调整为大堆。
Heap(T* a,size_t size){ assert(a); _a.reserve(size); for (size_t i=0;i<size;++i) {_a.push_back(a[i]); } //建堆,从第一个非叶子节点的父节点开始调整 for (int i=(size-2)/2;i>=0;--i) {AdjustDown(i); }}
void AdjustDown(size_t root){ size_t parent=root; size_t child=parent*2+1; //先让child指向左孩子 while (child<_a.size()) {//保证右孩子不为空if (child+1<_a.size() && _a[child+1]>_a[child]){ ++child;}if (_a[parent]<_a[child]){ std::swap(_a[parent],_a[child]); parent=child; child=parent*2+1;}else break;}}
2.建好堆后如何在堆中进行push元素?
先将元素插入到数组最后一个位置,然后进行上调操作,直至满足大堆的特点。
3.pop元素?
1>我们采取先将堆中最后一个元素转移至第一个元素位置,也就是用最后一个元素覆盖掉第一个元素;
2>将最后那个pop掉;
3>从0位置处开始往下调整;
4>在向下调整时,需注意找公共父节点parent=(child-1)/2,以及循环条件。
void Pop(){assert(!_a.empty());std::swap(_a[0],_a[_a.size()-1]);_a.pop_back();AdjustDown(0);}
void AdjustUp(size_t child){while (child>0){size_t parent=(child-1)/2;if (_a[parent]<_a[child]){std::swap(_a[parent],_a[child]);child=parent;}elsebreak;}}
实现大堆的完整代码:
Heap.h
#pragma once#include<iostream>#include<assert.h>#include<vector>using namespace std;template<class T>class Heap{public:Heap(T* a,size_t size){assert(a);_a.reserve(size);for (size_t i=0;i<size;++i){_a.push_back(a[i]);}//建堆,从第一个非叶子节点的父节点开始调整for (int i=(size-2)/2;i>=0;--i) {AdjustDown(i);}}void Push(const T& d){_a.push_back(d);AdjustUp(_a.size()-1);}void Pop(){assert(!_a.empty());std::swap(_a[0],_a[_a.size()-1]);_a.pop_back();AdjustDown(0);}const T& Top(){assert(!_a.empty());return _a[0];}bool Empty(){return _a.empty();}size_t Size(){return _a.size();}void Print(){for (size_t i=0;i<_a.size();++i){cout<<_a[i]<<" ";}cout<<endl;}protected:void AdjustDown(size_t root){size_t parent=root;size_t child=parent*2+1; //先让child指向左孩子while (child<_a.size()){//保证右孩子不为空if (child+1<_a.size() && _a[child+1]>_a[child]){++child;}if (_a[parent]<_a[child]){std::swap(_a[parent],_a[child]);parent=child;child=parent*2+1;}elsebreak;}}void AdjustUp(size_t child){while (child>0){size_t parent=(child-1)/2;if (_a[parent]<_a[child]){std::swap(_a[parent],_a[child]);child=parent;}elsebreak;}}protected:vector<T> _a;};
建小堆,其实思想都类似,所以可以采取一种方式,增强代码的复用性,即定义一个仿函数Less,larger实现数据的比较,从而实现父节点与子节点的交换,进而满足大小堆的特点。
#pragma once#include<iostream>#include<assert.h>#include<vector>using namespace std;template<class T>struct Less{bool operator()(const T& l,const T& r){return l<r;}};template<class T>struct Larger{bool operator()(const T& l,const T& r){return l>r;}};template<class T,class compare=Larger<T>>class Heap{public:Heap(T* a,size_t size){assert(a);_a.reserve(size);for (size_t i=0;i<size;++i){_a.push_back(a[i]);}//建堆,从第一个非叶子节点的父节点开始调整for (int i=(size-2)/2;i>=0;--i) {AdjustDown(i);}}void Push(const T& d){_a.push_back(d);AdjustUp(_a.size()-1);}void Pop(){assert(!_a.empty());std::swap(_a[0],_a[_a.size()-1]);_a.pop_back();AdjustDown(0);}const T& Top(){assert(!_a.empty());return _a[0];}bool Empty(){return _a.empty();}size_t Size(){return _a.size();}void Print(){for (size_t i=0;i<_a.size();++i){cout<<_a[i]<<" ";}cout<<endl;}protected:void AdjustDown(size_t root){compare com;size_t parent=root;size_t child=parent*2+1; //先让child指向左孩子while (child<_a.size()){//保证右孩子不为空,比较左右孩子if (child+1<_a.size() && com(_a[child+1],_a[child])){++child;}if (com(_a[child],_a[parent])){std::swap(_a[parent],_a[child]);parent=child;child=parent*2+1;}elsebreak;}}void AdjustUp(size_t child){while (child>0){compare com;size_t parent=(child-1)/2;if (com(_a[child],_a[parent])){std::swap(_a[parent],_a[child]);child=parent;}elsebreak;}}protected:vector<T> _a;};
测试:
#include"Heap.h"void Test1(){int a[] = {10,11,13,12,16,18,15,17,14,19};Heap<int> hp(a,sizeof(a)/sizeof(a[0])); //默认大堆hp.Print();cout<<"Empty> "<<hp.Empty()<<endl;cout<<"Size> "<<hp.Size()<<endl;cout<<"Top> "<<hp.Top()<<endl;hp.Push(20);hp.Print();hp.Pop();hp.Print();}void Test2(){int a[] = {10,11,13,12,16,18,15,17,14,19};Heap<int,Less<int>> hp(a,sizeof(a)/sizeof(a[0]));hp.Print();cout<<"Empty> "<<hp.Empty()<<endl;cout<<"Size> "<<hp.Size()<<endl;cout<<"Top> "<<hp.Top()<<endl;hp.Push(9);hp.Print();hp.Pop();hp.Print();}int main(){//Test1();Test2();system("pause");return 0;}
下面谈谈堆的建立,Push,Pop时间复杂度问题:
1.在建堆的过程,时间复杂度为O(n*lgn);
2.在Push时,时间复杂度为O(lgn);
3.在Pop时,时间复杂度为O(lgn).
再引入一个优先级队列的问题,队列本来是“先进先出”的,但是在一些特殊情况下排在队中间的需要比对头元素先出去,这就是优先级问题。
以前解决的方法是:1.在优先位置插入元素,时间复杂度为O(n);在队头删除元素,时间复杂度O(1).
2.在队尾插入元素,时间复杂度O(1);先找到优先出队列元素,再Pop,时间复杂度为O(n).
这两种方法,总的来说Push,Pop时间复杂度都为O(n).
学习堆之后,我们解决这个问题就可以更高效了,堆的Push,Pop总的时间复杂度才O(lgn).相比上面的方法,在处理大量数据时这种方法很占优势。
在堆得基本操作实现后,优先级队列可实现为:
template<class T,class compare=Larger<T>>class PriorityQueue{public:PriorityQueue(T* a,size_t size):_h(a,size){}void Push(const T& d){_h.Push(d);}void Pop(){_h.Pop();}bool Empty(){return _h.Empty()==0;}size_t Size(){return _h.Size();}const T& Front(){return _h.Top();}void PrintPriorityQueue(){_h.Print();}protected:Heap<T,compare> _h;};
测试用例:
void Test3(){int a[] = {10,11,13,12,16,18,15,17,14,19};PriorityQueue<int,Larger<int>> h(a,sizeof(a)/sizeof(a[0]));h.PrintPriorityQueue(); //19 17 18 14 16 13 15 12 10 11cout<<"Empty> "<<h.Empty()<<endl;cout<<"Size> "<<h.Size()<<endl;cout<<"Top> "<<h.Front()<<endl;h.Push(20); //20 19 18 14 17 13 15 12 10 11 16h.PrintPriorityQueue();h.Pop();h.PrintPriorityQueue();PriorityQueue<int,Less<int>> h1(a,sizeof(a)/sizeof(a[0]));h1.PrintPriorityQueue(); //10 11 13 12 16 18 15 17 14 19h1.Push(9); h1.PrintPriorityQueue(); //9 10 13 12 11 18 15 17 14 19 16h1.Pop();h1.PrintPriorityQueue();}
- 堆的基本实现及优先级队列问题
- 堆的实现及应用(优先级队列,堆排,TopK问题)
- 堆实现的优先级队列
- 堆排序及优先级队列Java实现
- 堆实现的最小优先级队列
- 堆的实现以及优先级队列
- 数据结构实现(优先级队列及堆排序类模板)
- 堆--优先级队列--topK问题
- 堆-实现优先级队列算法
- 优先级队列之堆实现
- 用堆实现优先级队列
- 堆与优先级队列实现
- 基于堆实现的优先级队列:PriorityQueue 解决 Top K 问题
- 基于堆实现的优先级队列:PriorityQueue 解决 Top K 问题
- 大小堆的实现与实现优先级队列
- 一般队列和循环队列及优先级队列的实现
- 基于堆的优先级队列
- 堆(优先级队列) 的应用
- Android 沉浸式状态栏攻略 让你的状态栏变色吧【转自鸿洋大神】
- [RK3288][Android6.0] USB UVC Camera是否支持的查询方法
- Path Sum I,II
- PHP变量
- 共享指针
- 堆的基本实现及优先级队列问题
- time_t和SYSTEMTIME之间的相互转换
- Opencv2.4.9源码分析
- DB2大数据量处理综述及关键技术探讨
- std list 排序
- Mysql show Status参数详解
- Oracle排名函数(Rank)实例详解
- Java虚拟机类加载机制
- 快速了解Log4J