堆——最大堆/最小堆的初始化、增加、删除等基本操作
堆是一种经过排序的完全二叉树或满二叉树
满二叉树即除最后一层无任何子节点外,每一层上的所有结点都有两个子结点的二叉树
堆排序运行的时间:O(nlog n)
1、示例:
如下为一个最大堆,用数组则可以按顺序表示堆{100,19,36,17,3,25,1,2,7}
2、堆初始化【堆排序的思想】
从一个无序序列初始化为一个堆的过程就是一个反复“筛选”的过程。由完全二叉树的性质可知,一个有n个节点的完全二叉树的最后一个非页节点就是n/2(比如9/2=4)节点开始。上图为无序数组{49,38,65,97,76,13,27,50}的初始化过程:
首先,未处理的数组对应的堆为图1模样。从第四个节点开始([8/2]=4),因为50 < 97,故要交换两节点,交换后还要继续对其新的左子树进行类似输出后那样的筛选。易见其左子树只有节点97,已经为最佳情况,故可以继续堆的初始化,如图2。再考虑第三个节点,因为13 < 27 < 65,即节点13为当前的最小节点,故与节点65交换,并对新的左子树进行筛选,其也为最佳情况,故可继续堆的初始化,结果如图3。然后考虑第二个节点,因为38 < 50 < 76,故已经为最优情况,不用调整。最后再考虑第一个节点,根节点。因为 13 < 38 < 49,故需要将根节点49与其右孩子节点13交换,交换后还要继续对其新的右子树进行类似输出后那样的筛选,可见右子树还需要调整,因为 27 < 49 < 65,故将节点49与节点27交换。此时已经处理完了根节点,初始化结束。最终结果如图5。
以下假设完成最大堆
3、元素上移操作
【参考《算法分析于与设计》P64】
//输入:数组以及需要被上移的元素下标i
template <class Type>
void sift_up(Type H[],int i)
{
BOOL done=FALSE;
if(i!=1){
while(!done && i!=1){
if(H[i]>H[i/2])
swap(H[i],H[i/2]);
else done=THRE;
i=i/2;
}
}
}
4、元素下移操作
template <class Type>
void sift_down(Type H[],int i)
{
BOOL done=FALSE;
if((2*i)<=n){
while(!done && (i=2*i)<=n){
if((i+1<=n)&&(H[i+1]>H[i]))
i=i+1;
if(H[i]>H[i/2])
swap(H[i/2],H[i]);
else done=THRE;
}
}
}
5、元素插入操作
template <class Type>
void insert(Type H[],int &n,Type x)
{
n=n+1;
H[n]=x;
sift_up(H,n);
}
6、元素删除操作
//输入:数组、数组的元素个数n,被删除的元素的下标
//输出:维持堆的性质的数组,以及删除后的元素的个数
template <class Type>
void delete(Type H[],int i)
{
Type x,y;
x=H[i];
y=H[n];
n=n-1;
if(i<=n){
H[i]=y;
if(y>x)
sift_up(H,i);
else
sift_down(H,n,i);
}
7、删除关键字最大元素
template <class Type>
Type delete_max(Type H[],int i)
{
Type x;
x=H[1];
delete(H[],n,1);
return x;
}
8、堆的建立
方法1
//堆的建立是指,把数组中的n个元素连续的使用插入操作插入队中
template <class Type>
void make_heap(Type A[],Type H[],int n)
{
int i,m=0;
for(i=0;i<n;i++)
insert(H,m,A[i]); //使用insert
}
方法2
template <class Type>
void make_heap(Type A[],int n)
{
int i;
A[n]=A[0];
for(i=n/2;i>=1;i--)
sift_down(A,i);
}
8、堆的排序
//输入:数组,元素个数
//输出:递增的数组
template <class Type>
void heap_sort(Type A[],int n)
{
int i;
make_heap(A,n);
for(i=n;i>1;i--)
swap(A[1],A[i];
sift_down(A,i-1,1);
}