基于论文“Min-Max Heaps and Generalized Priority Queues”
- 定义
- 抽象数据类型
- 源代码
- 截图
- 应用
定义
values stored at nodes on even (odd) levels are smaller than or equal to (respectively, greater than) values stored at their descendants.
抽象数据类型
template<class T>
class min_max_heap{
public:
min_max_heap(int initialCapacity = 10);//默认参数为10
~min_max_heap() { delete[] heap; }//析构函数
bool empty() const { return heapSize == 0; }//判断堆是否为空
int size() const {//返回堆的大小
return heapSize;
}
T* getHeap() const {
return heap;
}
void initialize(T *theHeap,int theSize);//根据已知数组建堆
const T& getMax();//得到最大值
const T& getMin();//得到最小值
void insert(T&);//插入值
const T& popMin();//删除最小值
const T& popMax();//删除最大值
void output(ostream& out);//输出
public:
int heapSize;//堆大小
int arrayLength;//堆的最大值
T *heap;//堆数组
};
源代码
template<class T>
min_max_heap<T>::min_max_heap(int initialCapacity) {//构造函数
if (initialCapacity < 1) {//初始大小必须大于0
ostringstream s;
s << "Error : Initial capacity = " << initialCapacity << " Must be > 0";
throw s.str();
}
arrayLength = initialCapacity + 1;//
heap = new T[arrayLength];
heapSize = 0;
}
int is_min_level(int i) {//返回下标为i的数据的层(大层还是小层)
if ((int)(floor(log(i) / log(2))) % 2) {
return 0;
}
else {
return 1;
}
}
template<class T>
int index_min_child_grandchild(min_max_heap<T>* h ,int i) {//找出下标为i的结点的儿子和孙子中最小的值
int a = first_child(i);
int b = second_child(i);
int d = second_child(a);
int c = first_child(a);
int f = second_child(b);
int e = first_child(b);
int min_idx = -1;
if (a <= h->heapSize) min_idx = a;
if (b <= h->heapSize && h->heap[b] < h->heap[min_idx]) min_idx = b;
if (c <= h->heapSize && h->heap[c] < h->heap[min_idx]) min_idx = c;
if (d <= h->heapSize && h->heap[d] < h->heap[min_idx]) min_idx = d;
if (e <= h->heapSize && h->heap[e] < h->heap[min_idx]) min_idx = e;
if (f <= h->heapSize && h->heap[f] < h->heap[min_idx]) min_idx = f;
return min_idx;
}
template<class T>
int index_max_child_grandchild(min_max_heap<T>* h, int i) {//找出下标为i的结点的儿子和孙子中最小的值
int a = first_child(i);
int b = second_child(i);
int d = second_child(a);
int c = first_child(a);
int f = second_child(b);
int e = first_child(b);
int max_idx = -1;
if (a <= h->heapSize) max_idx = a;
if (b <= h->heapSize && h->heap[b] > h->heap[max_idx]) max_idx = b;
if (c <= h->heapSize && h->heap[c] > h->heap[max_idx]) max_idx = c;
if (d <= h->heapSize && h->heap[d] > h->heap[max_idx]) max_idx = d;
if (e <= h->heapSize && h->heap[e] > h->heap[max_idx]) max_idx = e;
if (f <= h->heapSize && h->heap[f] > h->heap[max_idx]) max_idx = f;
return max_idx;
}
template<class T>
void swap(min_max_heap<T>* h, int i, int m) {//交换堆中下标为i和m的数据
T temp = h->heap[i];
h->heap[i] = h->heap[m];
h->heap[m] = temp;
if(flagPrint)
cout << " heap[" << i << "]:" << h->heap[m] << "<-->" << "heap[" << m << "]:" << h->heap[i] << endl <<"h:\n"<< *h << endl;
}
/*
小层下滤,先找到自己儿子和孙子中最小的值。
如果该值为儿子,证明自己没有孙子,这时只需比较该最小值与自己,如果该最小值小,则与自己交换位置,下滤结束;否则不变化,下滤结束;
如果该值为孙子,则比较该最小值与自己,如果该最小值大于自己,则证明以该元素为根节点的树的最小值就是该元素,下滤完成;否则如果该最小值小于自己,则进行交换位置,交换后判断自己与父亲结点的大小关系:
如果父亲节点(大层)小于自己,则交换位置,交换后只有该父亲节点元素位置不正确,所以对父亲节点元素重新进行下滤;
否则父亲节点大于自己,则对自己重新进行下滤操作。
*/
template<class T>
void TrickleDownMin(min_max_heap<T>* h,int i) {//小层下滤(用于删除)
int m = index_min_child_grandchild(h,i);//得到儿子和孙子中最小的值
if (m <= -1) return;
if (m > second_child(i)) {//如果孙子最小
if (h->heap[m] < h->heap[i]) {
swap(h, i, m);
if (h->heap[m] > h->heap[parent(m)]) {
swap(h, m, parent(m));
}
TrickleDownMin(h, m);
}
}
else {//如果儿子最小
if (h->heap[m] < h->heap[i]) {
swap(h, i, m);
}
}
}
template<class T>
void TrickleDownMax(min_max_heap<T>* h,int i) {//大层下滤(用于删除)
int m = index_max_child_grandchild(h, i);//得到儿子和孙子最大值
if (m <= -1) {
return;
}
if (m > second_child(i)) {//如果孙子最大
if (h->heap[m] > h->heap[i]) {
swap(h, i, m);
if (h->heap[m] < h->heap[parent(m)]) {
swap(h, m, parent(m));
}
TrickleDownMax(h, m);
}
}
else {//如果儿子最大
if (h->heap[m] > h->heap[i]) {
swap(h, i, m);
}
}
}
template<class T>
void trickleDown(min_max_heap<T>* h,int i) {//下滤,用于删除
//cout << "先判断是删除了最小元素,还是最大元素" << endl;
if (is_min_level(i)) {
TrickleDownMin(h, i);
}
else {
TrickleDownMax(h, i);
}
}
template<class T>
void bubbleup_min(min_max_heap<T>* h, int i) {//小层上滤
int pp_i = parent(parent(i));
if (pp_i <= 0) return;
if (h->heap[i]<h->heap[pp_i]) {//如果祖父比自己大,则交换
swap(h, i, pp_i);
bubbleup_min(h, pp_i);
}
}
template<class T>
void bubbleup_max(min_max_heap<T>* h, int i) {//大层上滤
int pp_i = parent(parent(i));
if (pp_i <= 0) return;
if (h->heap[i]>h->heap[pp_i]) {//如果祖父比自己小则交换
swap(h, i, pp_i);
bubbleup_max(h, pp_i);
}
}
template<class T>
void BubbleUp(min_max_heap<T>* h, int i) {//上滤,可用于插入
int p_i = parent(i);
if (p_i <= 0) return;
if (is_min_level(i)) {//如果是小层,进行父亲节点与自己的值大小比较
if (h->heap[i] > h->heap[p_i]) {//若父节点小,则交换,并进行大层上滤
swap(h, i, p_i);
bubbleup_max(h, p_i);
}
else {//如果父节点大则进行小层上滤
bubbleup_min(h, i);
}
}
else {//否则是大层
if (h->heap[i] < h->heap[p_i]) {//若父亲节点大于自己,则交换,并进行小层上滤
swap(h, i, p_i);
bubbleup_min(h, p_i);
}
else {//否则父亲节点小于自己,进行大层交换
bubbleup_max(h, i);
}
}
}
template<class T>
void min_max_heap<T>::initialize(T *theHeap, int theSize) {//初始化堆
delete[] heap;
heap = theHeap;
heapSize = theSize;
for (int root = heapSize / 2; root >= 1; root--)
{
trickleDown(this, root);
}
}
template<class T>
void min_max_heap<T>::insert(T& Element) {//插入
if (flagPrint) {
cout << "插入操作-插入";
cout << Element;
cout << ":" << endl;
}
//如果堆满可增加数组的长度,此处扩展为原先的2倍;
if (heapSize == arrayLength - 1) {
changeArrayLength(heap, arrayLength, 2 * arrayLength);
arrayLength *= 2;
}
int currentNode = ++heapSize;
heap[currentNode] = Element;
if(flagPrint)
cout <<"h:\n"<<*this<<endl;
BubbleUp(this, currentNode);
}
template<class T>
const T& min_max_heap<T>::getMax() {//返回最大值
printf("得到最大值操作:");
if (heapSize > 2) {//比较heap[2]和heap[3]
printf("%d\n", heap[2] < heap[3] ? heap[3] : heap[2]);
return heap[2] < heap[3] ? heap[3] : heap[2];
}
if (heapSize == 2) {
printf("%d\n", heap[2]);
return heap[2];
}
if (heapSize == 1) {
printf("%d\n", heap[1]);
return heap[1];
}
throw "错误:空堆不能得到最大值\n";
}
template<class T>
const T& min_max_heap<T>::getMin() {//返回根节点(即第一个元素)即最小元素
if (heapSize > 0) {
cout << "得到最小值操作:" <<heap[1] << endl;
return heap[1];
}
throw "错误:空堆不能得到最小值\n";
//printf("错误:空堆\n");
//return NULL;
}
template<class T>
const T& min_max_heap<T>::popMin() {//删除最小值
if (flagPrint)
cout << "删除最小值操作" << endl;
if (heapSize > 1) {
T d = heap[1];
//cout << "将根节点" << heap[1] << "(最小值)替换为最后一个元素" << heap[heapSize] << ",开始下滤:" << endl;
heap[1] = heap[heapSize--];
if(flagPrint)
cout << "heap[1] = "<<heap[1]<< endl <<"h:\n"<<*this << endl;
trickleDown(this, 1);
return d;
}
if (heapSize == 1) {
if (flagPrint)
cout << "只有一个元素,故删除后堆为空" << endl;
heapSize--;
return heap[1];
}
throw "错误:空堆不能删除最小值\n";
//printf("错误:空堆\n");
//return NULL;
}
template<class T>
const T& min_max_heap<T>::popMax() {//删除最大值
if (flagPrint)
cout << "删除最大值操作" << endl;
if (heapSize > 2) {
int index = 2;
if (heap[2] < heap[3]) index = 3;
T d = heap[index];
heap[index] = heap[heapSize--];
if (flagPrint)
cout << "heap["<<index<<"] = " << heap[index] << endl <<"h:\n"<<*this << endl;
trickleDown(this, index);
return d;
}
if (heapSize == 2) {
if (flagPrint)
cout << "两个元素,返回heap[2]即可" << endl;
heapSize--;
return heap[2];
}
if (heapSize == 1) {
if (flagPrint)
cout << "只有一个元素,故删除后堆为空" << endl;
heapSize--;
return heap[1];
}
throw "错误:空堆不能得到最小值\n";
}
//template<class T>
template<class T>
void min_max_heap<T>::output(ostream& out) {//输出堆元素
T **heapTreeMatrix;
if (heapSize == 0) throw "Error : empty heap not output!";
int level = 0;
int high = (int)(floor(log(heapSize) / log(2)));//high从0层开始
int bottle_node_count_max = pow(2, high);//最下层最多可以拥有的结点数
heapTreeMatrix = new T*[high + 1];
for (int i = 0; i < high + 1; i++) {
heapTreeMatrix[i] = new T[bottle_node_count_max * 2];
}
T *t = new T(INF);
for (int i = 0; i < high+1 ; i++) {
for (int j = 0; j < bottle_node_count_max*2; j++) {
heapTreeMatrix[i][j] = *t;
}
}
int index = 1;
while (level < high + 1) {
int inscrease = bottle_node_count_max * 2 / pow(2, level);
for (int j = inscrease/2 ; j < bottle_node_count_max * 2; j += inscrease) {
if(index<=heapSize)
heapTreeMatrix[level][j] = heap[index++];
}
level++;
}
for (int i = 0; i < high + 1; i++) {
for (int j = 0; j < bottle_node_count_max * 2; j++) {
if (heapTreeMatrix[i][j] == *t) {
printf(" ");
}
else {
out << setw(4) << heapTreeMatrix[i][j];
}
}
printf("\n");
}
for (int i = 0; i < high + 1; i++) {
delete[] heapTreeMatrix[i];
}
delete[] heapTreeMatrix;
}
template<class T>
ostream& operator<<(ostream& out, min_max_heap<T>& x) {//<<运算符重载
x.output(out);
out << endl;
return out;
}
测试代码
char choose;
min_max_heap<int> h(10);
int initSize = 0;//初始化数组元素个数
int array[2000];//初始化数组
int main(){
while (1) {
printf("****************************************************************************************\n");
printf("菜单 小大根交替堆实现双端优先队列\n");
printf("** u 建立小大根交替堆\n");
printf("** i 插入元素\n");
printf("** b 删除最大值\n");
printf("** s 删除最小值\n");
printf("** g 得到最大值\n");
printf("** h 得到最小值\n");
printf("** q 退出\n");
printf("****************************************************************************************\n");
cin >> choose;
int a;//临时存放int值
char c;//临时存放char值
try{
switch (choose) {
case 'u':
printf("建立小大根交替堆:\n");
printf("请输入关键值:\n");
getchar();
flagPrint = false;
while (1) {
scanf("%d", &a);
c = getchar();
//h.insert(a);
array[++initSize] = a;//初始化数组
if (c == '\n') break;
}
h.initialize(array, initSize);
cout << "建堆如下:"<<endl << h << endl;
//printf("按f回到主菜单\n");
break;
case 'i':
printf("插入:");
scanf("%d", &a);
flagPrint = true;
h.insert(a);
break;
case 'b':
flagPrint = true;
a = h.popMax();
printf("删除元素为%d\n", a);
break;
case 's':
flagPrint = true;
a = h.popMin();
printf("删除元素为%d\n", a);
break;
case 'h':
h.getMin();
break;
case 'g':
h.getMax();
break;
case 'q':
return 0;
default:
printf("输入有误,请重新输入\n");
break;
}
}
catch (string s) {
cout << s << endl;
}
catch (char* s) {
cout << s << endl;
}
}
return 0;
}
测试截图
应用
可以用来实现双端优先队列Double_Ended_Priority_Queue,方便的得到最大最小值。