堆与优先队列
堆
一、概念特性
–以最小堆为例
-
最小堆是一个关键码序列(数组),第i个元素Ki,满足
- Ki<=K2i+1
- Ki<=K2i+2
-
可以用完全二叉树来表示最小堆,即逻辑结构上来看,堆实际上是一种树形结构
二、堆的定义
template class<T>
class MinHeap{
private
T* heapArray; // 存放堆数据的数组
int CurrentSize; // 当前堆中元素数目
int MaxSize; // 堆所能容纳的最大元素数目
void BuildHeap(); // 建堆
public:
MinHeap(const int n); // 构造函数,n为最大元素数目
virtual ~MinHeap(){delete []heapArray;}; // 析构函数
int leftchild(int pos) const; // 返回左孩子位置
int rightchild(int pos) const; // 返回右孩子位置
int parent(int pos) const; // 返回父结点位置
bool Remove(int pos, T& node); // 删除给定下标的元素
bool Insert(const T& newNode); // 向堆中插入新元素newNode
T& RemoveMin(); // 从堆顶删除最小值
void SiftUp(int position); // 从position向上开始调整,使序列成为堆
void SiftDown(int left); // 筛选法函数,参数left表示开始处理,使其成为堆
}
三、筛选法使数组(完全二叉树)成为堆
–注意这里仅仅是对某一个位置的元素进行位置的调整,整个数组的调整还需要用一个循环
从position位置向下筛选
-
将position记为i,并将其数据存放至temp中,
-
找到结点i的两个子节点中较小的数j,
-
判断j是否还在数组中(j与maxsize比较),在则将较小的数赋值给父结点,不在数组则5
-
令i=j,j=2*j+1,向下继续2
-
将1中方储存的temp中的值放到i结点中
void MinHeap<T>::SiftDown(int position) { int i = position; // 标识父结点 int j = 2*i+1; // 标识关键值较小的子结点 T temp = heapArray[i]; // 保存父结点 while (j < CurrentSize) { if((j < CurrentSize-1)&&(heapArray[j] > heapArray[j+1])) j++; // j指向数值较小的子结点 if (temp > heapArray[j]) { heapArray[i] = heapArray[j]; i = j; j = 2*j + 1; // 向下继续 } else break; } heapArray[i]=temp; }
从position向上调整
-
同SiftDown类似,
template<class T> void MinHeap<T>::SiftUp(int position) { // 从position向上开始调整,使序列成为堆 int temppos=position; // 不是父子结点直接swap T temp=heapArray[temppos]; while((temppos>0) && (heapArray[parent(temppos)] > temp)) { heapArray[temppos]=heapArray[parent(temppos)]; temppos=parent(temppos); } heapArray[temppos]=temp;// 找到最终位置 }
四、最小堆的建立、插入和删除元素
建立
-
将n个数(关键码)放入数组中,用完全二叉树显示
-
所有叶结点本身就是堆,
-
主要从倒数第二层最右端(i=[n/2]-1)开始用siftDown()函数依次调整,直到整个完全二叉树为一个最小堆
template<class T> void MinHeap<T>:BuildHeap() { int i=CurrentSize/2-1; for(;i>=0;i--) SiftDown(i);//SiftDown()函数只对某一位置进行调整,使其满足最小堆的要求,要使整个数组为最小堆,要用一个循环,需要从倒数第二层最右端位置调用SiftDown()函数,直到头。 }
插入元素
- 判断数组是否满了
- 将新元素加入数组的最后,用Siftup()向上调整
heapArray[CurrentSize]=newNode;SiftUp(CurrentSize); // 向上调整
删除元素
-
将数组的最后一个数拿到要删除的位置
-
判断该数和其父结点的大小,如果大于其父结点的值,说明该结点和上面的结点的值摆放没问题,但其下面的数摆放必定出了问题因此从这个结点向下调整SiftDown,如果小于,同理,SiftUp
-
上面这个例子,分别删除68 16 进行比较
template<class T> bool MinHeap<T>::Remove(int pos ,T& node){ if(pos<0||pos>CurrentSize) return false; T temp=heapArray[pos]; heapArray[pos]=heapArray[--CurrentSize];//数组个数CurrentSize个,最后一个表示为heapArray[CurrentSize-1] if(heapArray[pos]<heapArray[parent(pos)] SiftUp(pos); else SiftDown(pos); node=temp return true; }
五、时间复杂度–时间代价
- 时间代价为O(n)
- 由于堆只有logn层,故删除、插入元素时间代价都为O(logn)
优先队列
- 堆可以用于实现优先队列,根结点就是最大值或者最小值
- 优先队列能让一个队列中,最小值(最大值)最先出队