最大堆:是指在一颗完全二叉树中,一个结点的关键值都不小于其儿子结点的关键字值,既满足(parent.value>=parent->left_child.value&&parent.value>=parent->right_child.value.).
最小堆:是指在一颗完全二叉树中,一个结点的关键值都不大于其儿子结点的关键字值,既满足(parent->left_child.value>=parent.value&&parent->right_child.value=>parent.value).
引入一个话题,在优先级调度队列中,由于经常涉及删除和插入操作,故插入和删除的时间复杂度是影响其调度性能重要因素,但堆在优先级调度队列中却是一种很受欢迎的存储表示方式,在存储方式,数组,链表都可以实现优先级调度队列,但只有堆在插入或删除操作都能在O(log2n)时间内完成,下面是一个最高优先级调度队列实现操作方式的对比表格:
存储表示 | 插入操作 | 删除操作 |
---|---|---|
无序数组 | O(1) | O(N) |
无序单向链表 | O(1) | O(N) |
有序数组 | O(N) | O(1) |
有序单向链表 | O(N) | O(1) |
最大堆 | O(log2n) | O(log2n) |
初始化最大堆或最小堆,都是从一个初始化的二叉树中的最后一个非叶子结点继续调整,如果是最大堆,让最后一个非叶子结点跟其两个儿子比较,找到最大的那个结点,然后使其最大结点作为父结点,如果父结点有跟子结点有交换过,还有让其子结点继续作比较来调整直到满足堆条件或到了最后一个结点,否则该结点调整完毕,然后再从最靠后的第二个非叶子结点做同样的比较,挑选最大结点作为父结点,直到满足堆条件或到了最后一个结点,最小堆类似,但是让最后一个非叶子结点跟其两个儿子比较,找到最小的那个结点,然后使其最小结点作为父结点,如果父结点有跟子结点有交换过,还有让其子结点继续作比较来调整直到满足堆条件或到了最后一个结点,否则该结点调整完毕,然后再从最靠后的第二个非叶子结点做同样的比较,挑选最小结点作为父结点,直到满足堆条件或到了最后一个结点,时间复杂度O(n).
就初始建立一个最大堆为例,整个调整过程如图:
代码实现:
/**
注意:堆数据初始的数组的元素
索引是从1开始,而不是从0开始
故一个结点索引为i的元素,其左
结点的索引为2*i,右结点的索引为
2*i+1
*/
#include <iostream>
#include<stdlib.h>
#include<stdio.h>
#define MAX_ELEMENTS 200
#define HEAP_FULL(n) (n==MAX_ELEMENTS-1)
#define HEAP_EMPTY(n) (!n)
//堆数据结构体
typedef struct
{
int key;
}element;
//堆最多存储元素容量
element heap[MAX_ELEMENTS];
using namespace std;
//调整堆
void adjustHeap(element *a,int m,int size)
{
element temp=a[m];
int i=m,j;
j=m*2;//其左结点索引
while(j<=size)
{
//选取较大的那个子结点
if(j+1<=size&&a[j].key<a[j+1].key)
j++;
if(temp.key>=a[j].key) break;//满足堆条件则跳出
else
{
a[i]=a[j];//作为父节点
i=j;//调整交换过的子结点
j=2*i;//要被调整的子结点的左儿子
}
}
a[i]=temp;
}
void createMaxHeap(element *a,int size)
{
//从二叉树中最后一个非叶子结点开始调整
for(int i=size/2;i>=1;i--)
adjustHeap(a,i,size);
}
最大堆或最小堆的插入操作就是先在堆树的尾端插入一个结点,然后跟父结点比较,如果满足堆条件或遇到根结点则结束,否则交换后继续比较,然后再重复前面的操作,时间复杂度为O(log2n).
就拿最大堆的插入操作为例,调整过程如图:
代码实现:
void insert_max_head(element item,int size)
{
if(HEAP_FULL(size))
{
fprintf(stderr,"The heap is full \n");
exit(1);
}
int i=++size;//获得最后一个结点索引
//比较新插入节点的值和父结点的值
//直到遇到根结点或满足堆条件
while(i!=1&&item.key>heap[i/2].key)
{
heap[i]=heap[i/2];
i/=2;
}
heap[i]=item;
}
最大堆或最小堆的删除操作就是删除最顶端元素,然后用堆树的最后一个结点补上,再做堆调整,其堆调整与建立堆一样,故这里不再重复描述,时间复杂度也为O(log2n).
也拿最大堆为例,删除操作如图:
代码实现:
element delete_max_head(int size)
{
if(HEAP_EMPTY(size))
{
fprintf(stderr,"The heap is empty \n");
exit(1);
}
element item=heap[1];//删除堆最顶端元素
heap[1]=heap[size];//将最后一个结点补为顶端元素
size--;
adjustHeap(heap,1,size);//调整堆使其满足堆条件
return item;
}