1.堆得介绍
堆得数据结构是一种数组对象,堆可以被看做一颗完全二叉树(借助二叉树的思想建堆以及插入和删除比较形象直观)
2.堆得分类:
①最大堆:每个父节点>它的孩子结点
②最小堆:每个父节点<它的孩子结点
3.堆得应用
①优先级队列
①堆排序
4.堆得底层是一个数组,学了STL库之后,可以将底层写层vector,可以动态增容
5.堆得创建
将一个数组中的元素进行向下调整,调成大堆或者小堆
6.时间复杂度:
建堆O(N*lgN); 插入O(lgN); 删除O(lgN)
实现:将一个数组中的的元素进行向下调整,就可以建成大堆或者小堆;然后如果要给建好的大堆或者小堆中插入元素,
则是把这个元素插到堆得尾端,然后从插入的尾端地方开始进行向上调整;如果是要删除堆顶的元素,则是要先把堆顶
的元素和堆尾的元素进行交换,然后删除堆尾的元素,然后从堆顶开始进行向下调整;重新或者一个大堆或者小堆;
有时候,我们既需要大堆,也需要小堆,当然可以实现两个类,大堆类和小堆类。但是这样代码的重复性太强;
仔细想了之后,我们发现大堆和小堆最大的区别就在于个结点与孩子结点的大小关系,其他的思路什么的都是一样的,两个类就达不到代码的复用性。
所以我们可以采用仿函数,又叫函数对象,通过它来实现代码复用。
7.代码实现
#include<iostream>
using namespace std;
#include<vector>
#include<cassert>
template<typename T>
struct Great//大堆
{
bool operator()(const T& left,const T& right)
{
return left>right;
}
};
template<typename T>//小堆
struct Small
{
bool operator()(const T& left,const T& right)
{
return left<right;
}
};
template<typename T,typename Compare=Great<T>>
class Heap
{
public:
Heap(const T* arr=NULL,int sz=0)//构造
{
//1.先将数组中的元素全部放入堆中
for (int i=0;i<sz;i++)
{
_heap.push_back(arr[i]);
}
//2.调整堆--建大堆或者小堆
for(int i=(sz-2)/2;i>=0;i--)
{
AdjustDown(i);//向下调整
}
}
void Push(const T& x)//给堆中插入元素
{
//1.先将该元素插入堆中
_heap.push_back(x);
//2.然后向上调整
AdjustUp(_heap.size()-1);
}
void Pop()//从堆中删除元素--大堆删除最大/小堆删除最小的元素
{
assert(_heap.size()>0);
//1.将尾元素放入第一个位置
_heap[0]=_heap[_heap.size()-1];
_heap.pop_back();//删掉尾元素
//2.从堆顶端向下调整
AdjustDown(0);
}
const T& Top()//返回堆顶的元素--最大值或者最小值
{
return _heap[0];
}
size_t Size()//返回堆中的元素个数
{
return _heap.size();
}
void Printf()//打印堆
{
for (size_t i=0;i<_heap.size();i++)
{
cout<<_heap[i]<<" ";
}
cout<<endl;
}
private:
//从堆的根结点开始向下调整
void AdjustDown(int father)//向下调整
{
Compare com;
size_t child=2*father+1;
while (child< _heap.size())
{
//寻找该节点的左右孩子中的最大值的下标
if (child+1<_heap.size()&&com(_heap[child+1],_heap[child]))
{
child++;
}
//比较左右孩子的最大值和父节点的大小,如果父节点小于,则进行交换,要么调整结束
if( child < _heap.size()&&com(_heap[child],_heap[father]))
{
swap(_heap[father],_heap[child]);
father=child;
child=2*father+1;
}
else
//当已经调整到堆底或者根结点不小于左右孩子的最大值,调整结束
break;
}
}
//从堆得最后一个结点开始向上调整
void AdjustUp(int child)//向上调整
{
Compare com;
int father=(child-1)/2;
while (child>0)
{
if (com(_heap[child],_heap[father]))
{
swap(_heap[child],_heap[father]);
child=father;
father=(child-1)/2;
}
else
break;
}
}
private:
vector<T> _heap;
};
int main()
{
int a [] = {10,11, 13, 12, 16, 18, 15, 17, 14, 19};
size_t sz=sizeof(a)/sizeof(a[0]);
Heap<int,Small<int>> hp(a,sz);
cout<<"原始堆:"<<endl;
hp.Printf();
cout<<"最值:"<<hp.Top()<<endl;
cout<<"堆元素个数:"<<hp.Size()<<endl;
//hp.Push(2);
//cout<<"向堆中插入2:"<<endl;
//hp.Printf();
//cout<<"最值:"<<hp.Top()<<endl;
//cout<<"堆元素个数:"<<hp.Size()<<endl;
cout<<"删除堆得最小值:"<<endl;
hp.Pop();
hp.Printf();
cout<<"最值:"<<hp.Top()<<endl;
cout<<"堆元素个数:"<<hp.Size()<<endl;
return 0;
}