参考博客: https://blog.csdn.net/weixin_41698717/article/details/107789354
本章问题
1.完全二叉堆的堆序性是什么
堆序性:堆顶之外的每个节点都不大于其父节点。
2.完全二叉堆中的上滤是如何操作的
对第i 个词条进行上滤,
查看该节点的父亲是否小于该节点,
1.如果小于该节点,与父节点交换,然后对父节点位置进行上滤
2.如果大于该节点,结束上滤, 并返回上滤最终抵达的位置
3.完全二叉堆的插入中是如何查找插入位置的
将词条插入末尾位置,然后对该词条进行上滤操作
4.完全二叉堆中的下滤是如何操作的
1.当左孩子节点存在时,若节点大于父亲,将父节点与左节点交换,之后判断右节点
2.当右孩子节点存在时,若节点大于父亲,将父节点与节点交换。
3.获取三者中的大者,若最大者若不为自己,二者换位,并继续考查下降后的 原父节点
5.完全二叉堆中的删除顶节点的是如何操作的
1.摘除堆顶(首词条),代之以末词条
2.对新堆顶实施下滤
6.Floyd是如何建堆的
从末节点依次遍历到头结点,对每个节点的父亲,依次进行下滤
7.PQ_ComplHeap是如何进行构造的
使用拷贝函数依次复制元素到堆中,然后进行Floyd建堆。
8.在PQ_ComplHeap.h文件中生成heapsort失败
将vector中堆排序接口权限改为public后,在测试调用堆排序成功,若是protected会出错
10.2.1 PQ_ComplHeap
//"pq.h"
#pragma once
template <typename T> struct PQ { //优先级队列PQ模板类
virtual void insert ( T ) = 0; //按照比较器确定的优先级次序插入词条
virtual T getMax() = 0; //取出优先级最高的词条
virtual T delMax() = 0; //删除优先级最高的词条
};
//"pq_macro.h"
#pragma once
#define InHeap(n, i) ( ( ( -1 ) < ( i ) ) && ( ( i ) < ( n ) ) ) //判断PQ[i]是否合法
#define Parent(i) ( ( i - 1 ) >> 1 ) //PQ[i]的父节点(floor((i-1)/2),i无论正负)
#define LastInternal(n) Parent( n - 1 ) //最后一个内部节点(即末节点的父亲)
#define LChild(i) ( 1 + ( ( i ) << 1 ) ) //PQ[i]的左孩子
#define RChild(i) ( ( 1 + ( i ) ) << 1 ) //PQ[i]的右孩子
#define ParentValid(i) ( 0 < i ) //判断PQ[i]是否有父亲
#define LChildValid(n, i) InHeap( n, LChild( i ) ) //判断PQ[i]是否有一个(左)孩子
#define RChildValid(n, i) InHeap( n, RChild( i ) ) //判断PQ[i]是否有两个孩子
#define Bigger(PQ, i, j) ( PQ[i] <= PQ[j] ? j : i ) //取大者(等时前者优先)
#define ProperParent(PQ, n, i) /*父子(至多)三者中的大者*/ \
( RChildValid(n, i) ? Bigger( PQ, Bigger( PQ, i, LChild(i) ), RChild(i) ) : \
( LChildValid(n, i) ? Bigger( PQ, i, LChild(i) ) : i \
) \
) //相等时父节点优先,如此可避免不必要的交换
10.2.1.1 PQ_ComplHeap ADT接口
操作接口 | 功能描述 |
---|---|
percolateDown(n,i ) | 对向量前n个词条中的第i个实施下滤,i < n |
percolateUp(i ) | 对向量中的第i个词条实施上滤操作 |
heapify(n ) | Floyd建堆算法 |
PQ_ComplHeap( ) | 默认构造 |
PQ_ComplHeap(A,n ) | 批量构造 |
insert ( T ) | 按照比较器确定的优先级次序,插入词条 |
getMax( ) | 读取优先级最高的词条 |
delMax( ) | 删除优先级最高的词条 |
10.2.1.2 PQ_ComplHeap类模板
#include "vector.h" //借助多重继承机制,基于向量
#include "pq_macro.h"
#include "pq.h" //按照优先级队列ADT实现的
template <typename T> class PQ_ComplHeap : public PQ<T>, public Vector<T> { //完全二叉堆
// /*DSA*/friend class UniPrint; //演示输出使用,否则不必设置友类
protected:
Rank percolateDown ( Rank n, Rank i ); //下滤
Rank percolateUp ( Rank i ); //上滤
void heapify ( Rank n ); //Floyd建堆算法
public:
PQ_ComplHeap() { } //默认构造
PQ_ComplHeap ( T* A, Rank n ) { this->copyFrom ( A, 0, n ); heapify ( n ); } //批量构造
void insert ( T ); //按照比较器确定的优先级次序,插入词条
T getMax(); //读取优先级最高的词条
T delMax(); //删除优先级最高的词条
}; //PQ_ComplHeap
10.2.1.3 getMax
//getMax()
template <typename T> T PQ_ComplHeap<T>::getMax() { return this->_elem[0]; }
10.2.1.4 insert
//insert
template <typename T> void PQ_ComplHeap<T>::insert ( T e ) { //将词条插入完全二叉堆中
Vector<T>::insert ( e ); //首先将新词条接至向量末尾
percolateUp ( this->_size - 1 ); //再对该词条实施上滤调整
}
10.2.1.5 delMax()
//delMax()
template <typename T> T PQ_ComplHeap<T>::delMax() { //删除非空完全二叉堆中优先级最高的词条
T maxElem = this->_elem[0]; this->_elem[0] = this->_elem[ --this->_size ]; //摘除堆顶(首词条),代之以末词条
percolateDown ( this->_size, 0 ); //对新堆顶实施下滤
return maxElem; //返回此前备份的最大词条
}
10.2.1.5 percolateUp()
//对向量中的第i个词条实施上滤操作,i < _size
template <typename T> Rank PQ_ComplHeap<T>::percolateUp ( Rank i ) {
while ( ParentValid ( i ) ) { //只要i有父亲(尚未抵达堆顶),则
Rank j = Parent ( i ); //将i之父记作j
if ( this->_elem[i] <= this->_elem[j] ) break; //一旦当前父子不再逆序,上滤旋即完成
swap ( this->_elem[i], this->_elem[j] ); i = j; //否则,父子交换位置,并继续考查上一层
} //while
return i; //返回上滤最终抵达的位置
}
10.2.1.6 percolateDown
//对向量前n个词条中的第i个实施下滤,i < n
template <typename T> Rank PQ_ComplHeap<T>::percolateDown ( Rank n, Rank i ) {
Rank j; //i及其(至多两个)孩子中,堪为父者
while ( i != ( j = ProperParent ( this->_elem, n, i ) ) ) //只要i非j,则
{ swap ( this->_elem[i], this->_elem[j] ); i = j; } //二者换位,并继续考查下降后的i
return i; //返回下滤抵达的位置(亦i亦j)
}
10.2.1.6 Floyd建堆
//Floyd建堆
template <typename T> void PQ_ComplHeap<T>::heapify ( Rank n ) { //Floyd建堆算法,O(n)时间
for ( int i = LastInternal ( n ); InHeap ( n, i ); i-- ) //自底而上,依次
/*DSA*/
percolateDown ( n, i ); //下滤各内部节点
// /*DSA*/for ( int k = 0; k < n; k++ ) {
// /*DSA*/ int kk = k; while ( i < kk ) kk = (kk - 1) / 2;
// /*DSA*/ i == kk ? print(_elem[k]) : print(" " );
// /*DSA*/}; printf("\n");
// /*DSA*/}
}
10.2.2 PQ_ComplHeap.h
#include "vector.h" //借助多重继承机制,基于向量
#include "pq_macro.h"
#include "pq.h" //按照优先级队列ADT实现的
template <typename T> class PQ_ComplHeap : public PQ<T>, public Vector<T> { //完全二叉堆
// /*DSA*/friend class UniPrint; //演示输出使用,否则不必设置友类
protected:
Rank percolateDown ( Rank n, Rank i ); //下滤
Rank percolateUp ( Rank i ); //上滤
void heapify ( Rank n ); //Floyd建堆算法
public:
PQ_ComplHeap() { } //默认构造
PQ_ComplHeap ( T* A, Rank n ) { this->copyFrom ( A, 0, n ); heapify ( n ); } //批量构造
void insert ( T ); //按照比较器确定的优先级次序,插入词条
T getMax(); //读取优先级最高的词条
T delMax(); //删除优先级最高的词条
}; //PQ_ComplHeap
//getMax()
template <typename T> T PQ_ComplHeap<T>::getMax() { return this->_elem[0]; }
//insert
template <typename T> void PQ_ComplHeap<T>::insert ( T e ) { //将词条插入完全二叉堆中
Vector<T>::insert ( e ); //首先将新词条接至向量末尾
percolateUp ( this->_size - 1 ); //再对该词条实施上滤调整
}
void swap( int &a, int &b){
int tmp = a;
a = b;
b = tmp;
}
//上滤
//对向量中的第i个词条实施上滤操作,i < _size
template <typename T> Rank PQ_ComplHeap<T>::percolateUp ( Rank i ) {
while ( ParentValid ( i ) ) { //只要i有父亲(尚未抵达堆顶),则
Rank j = Parent ( i ); //将i之父记作j
if ( this->_elem[i] <= this->_elem[j] ) break; //一旦当前父子不再逆序,上滤旋即完成
swap ( this->_elem[i], this->_elem[j] ); i = j; //否则,父子交换位置,并继续考查上一层
} //while
return i; //返回上滤最终抵达的位置
}
//delMax()
template <typename T> T PQ_ComplHeap<T>::delMax() { //删除非空完全二叉堆中优先级最高的词条
T maxElem = this->_elem[0]; this->_elem[0] = this->_elem[ --this->_size ]; //摘除堆顶(首词条),代之以末词条
percolateDown ( this->_size, 0 ); //对新堆顶实施下滤
return maxElem; //返回此前备份的最大词条
}
//对向量前n个词条中的第i个实施下滤,i < n
template <typename T> Rank PQ_ComplHeap<T>::percolateDown ( Rank n, Rank i ) {
Rank j; //i及其(至多两个)孩子中,堪为父者
while ( i != ( j = ProperParent ( this->_elem, n, i ) ) ) //只要i非j,则
{ swap ( this->_elem[i], this->_elem[j] ); i = j; } //二者换位,并继续考查下降后的i
return i; //返回下滤抵达的位置(亦i亦j)
}
//Floyd建堆
template <typename T> void PQ_ComplHeap<T>::heapify ( Rank n ) { //Floyd建堆算法,O(n)时间
for ( int i = LastInternal ( n ); InHeap ( n, i ); i-- ) //自底而上,依次
/*DSA*/
percolateDown ( n, i ); //下滤各内部节点
// /*DSA*/for ( int k = 0; k < n; k++ ) {
// /*DSA*/ int kk = k; while ( i < kk ) kk = (kk - 1) / 2;
// /*DSA*/ i == kk ? print(_elem[k]) : print(" " );
// /*DSA*/}; printf("\n");
// /*DSA*/}
}
template <typename T> void Vector<T>::heapSort ( Rank lo, Rank hi ) { //0 <= lo < hi <= size
PQ_ComplHeap<T> H ( _elem + lo, hi - lo ); //将待排序区间建成一个完全二叉堆,O(n)
while ( ! H.empty() ) //反复地摘除最大元并归入已排序的后缀,直至堆空
_elem[--hi] = H.delMax(); //等效于堆顶与末元素对换后下滤
}
10.2.3 PQ_ComplHeap.h测试
#include "pq_complheap.h"
#include <iostream>
using namespace std;
int main(){
int A[] = { 4, 2, 5, 1, 3};
//测试建堆函数
PQ_ComplHeap<int> a (A, 5);
//测试getMax
cout << a.getMax() << endl;//5
//测试insert
a.insert( 6 );
//测试delMax
cout << a.delMax() << endl;//6
cout << a.getMax() << endl;//5
//测试堆排序
Vector<int> vec(A, 5);
vec.heapSort( 0, 5);
for( int i= 0; i<5; ++i )
cout << vec[i] << " ";//1 2 3 4 5
}