堆排序算法
堆是一个数组,他可以被看成是一个近似的完全二叉树,除了最底层之外,该数是完全充满的。一般的,给定一颗二叉树的一个节点,可以很快的算出它的父、左子、右子节点。
Parent(i)=i/2
Left(i)=i*2
Right(i)=i*2+1
最大堆是指的除了根节点外的所有节点,都满足A(parent(i))>=A(i)。一个堆的高度也就是O(lgn)。一个堆排序算法的基本过程包括:维护最大堆MAX-HEAPIFY,时间复杂度为O(lgn); 构造一个最大堆BUILD-MAX-HEAP;原址排序堆过程HEAPSORT,时间复杂度为O(nlgn)。
维护最大堆
MAX-HEAPIFY用来维护最大的堆,它的输入是一个没有建立好的堆以及一个将要维护的位置。假定在调用它时,该即将维护的位置的左右子树都已经维护好了,但是该节点有可能小于左右子树。因此,需要将它和左右子树的最大值变换顺序,此时,子树也有可能不满足最大堆的条件,依次调用这个过程,使得该节点的子树能满足最大堆的要求。
MAX-HEAPIFY(i)
(MaxValue, MaxInd)=MAX(Right(i),Value(i),Left(i))
If MaxValue > Value(i)
SWAP(MaxValue,Value(i))
MAX-HEAPIFY(MaxInd)
建堆
BUILD-MAX-HEAP:维护最大堆时,假定该即将维护的位置的左右子树都已经维护好了。为了达到这一个假定的条件,可以对初始堆进行从底往上进行调用MAX-HEAPIFY(i)。假定堆的长度为LENGTH,则:
For i = LENGTH/2 : -1 : 1
MAX-HEAPIFY(i)
堆排序算法
通过以上两步,可以建立一个最大堆。堆的最大元素总是在a(1)中。因此,将a(1)取出,并将a(end)放到a(1)所在的位置上,然后对位置1进行维护最大堆,过程如下:
HEAPSORT
For i = LENGTH : -1:2
SWAP a(1) with a(LENGTH)
LENGTH–
MAX-HEAPIFY(1)
void Swap(int *pnA, int *pnB)
{
int temp = *pnA;
*pnA = *pnB;
*pnB = temp;
}
void MAX_HEAPIFY(int num[] , int thisIndex,int size)
{
int minIndex; // 指向左右子节点的最小位置
//如果存在子节点
while (thisIndex * 2 + 1 < size)
{
minIndex = thisIndex * 2 + 1; // 初始化为左子节点
if (thisIndex * 2 + 2 < size) // 如果有右节点
{
if (num[thisIndex * 2 + 1] > num[thisIndex * 2 + 2]) // 取最小的,并更新
{
minIndex = thisIndex * 2 + 2;
}
}
// 该数和最小者进行比较,
if (num[thisIndex] < num[minIndex])
{
break;//已经排序完毕
}
else
{
Swap(num + thisIndex , num + minIndex);// 交换两个数,让大数往下沉
thisIndex = minIndex; // 更新需要维护的节点
}
}// while
}
void BUILD_MAX_HEAP(int num[] ,int size)
{
int i;
//从最后一个非叶子节点开始,从下往上进行建堆
for (i = size / 2 - 1; i >= 0; i--)
{
MAX_HEAPIFY(num, i,size);// 进行维护该节点的操作
}
}
void HEAPSORT(int num[] ,int size)
{
int i;
int iLength = size;
BUILD_MAX_HEAP(num, size); // 建立最小堆
for (i = iLength - 1; i >= 1; i--)
{
Swap(num, num + i);// 交换
size--;//此时最小的元素在最尾,因此堆的规模减1
MAX_HEAPIFY(num, 0, size);// 维护新的首元素,并进行下滤操作
}
}