您的位置:首页 > 其它

堆排序

2016-03-28 20:24 295 查看
1.堆



堆排序集合了归并排序的优点:时间复杂度位O(nlgn)

插入排序的优点:具有空间原址性(任何时候只需要常数个额外的元素空间存储临时数据)

堆是一个数组,可以被看成是一个近似的完全二叉树。堆上的每个节点对应数组中的一个元素。除了最底层外,该树是完全充满的。这样给定一个结点的下标 i,我们很容易计算得到它的父结点、左孩子和右孩子的下标。

PARENT(i)

return ⌊i/2⌋

LEFT(i)

return 2i

RIGHT(i)

return 2i+1



最大堆性质:除了根节点以外的结点 i 都要满足:A [PARENT(i)] >= A[ i ],堆中最大元素存放在根结点 中



最小堆性质:除了根节点以外的结点 i 都要满足:A [PARENT(i)] <= A[ i ],堆中最小元素存放在根结点中

排序一般用最大堆实现,最小堆常用来构造优先队列。



2.维护堆的性质



堆中结点的高度:就是该结点到叶结点最长简单路径上边的数目。包含n个元素的堆高度为 lgn。

MAX-HEAPIFY过程:调整堆为最大堆,时间复杂度为 O(nlgn)。

BUILD-MAX-HEAP过程:从无序的输入数据数组中构造一个最大堆,具有线性时间复杂度。

HEAPSORT过程 :对一个数组进行原址排序,时间复杂度为 O(nlgn)。

MAX-HEAP-INSERT、HEAP-EXTRACT-MAX、HEAP_INCREASE-KEY和HEAP-MAXIMUM过程:利用堆实现一个优先队列,时间复杂度为O(lgn)。

 MAX-HEAPIFY(A,i)//MAX-HEAPIFY通过让A[i]的值在最大堆中“逐级下降”,从而使下标i为根节点的子树重新遵循最大堆的性质。

l=LEFT(i)

r=RIGHT(i)

if l<=A.heap-size and A[l]>A[i]

largest=l

else largest=i

if r<=A.heap-size and A[r]>A[largest]

largest=r

if largest !=i

exchange A[i] with A[largest]

MAX-HEAPIFY(A,largest)


在程序的每一步中,从A[i]、A[LEFT(i)]、A[RIGHT(i)]中选出最大的,并将其下标存储在largest中。如果A[i]是最大的,那么以i为根节点的子树已经是最大堆,程序结束,否则,最大元素是i的某一个孩子节点,交换A[i]和A[largest]的值,使其孩子都满足最大堆的性质。交换后,下标为largest的结点的值是原来的A[i],于是以该节点为根的子树又有可能会违反最大堆的性质。因此需对该子树递归调用MAX-HEAPIFY。过程如下图:





3.建堆

我们可以用自底向上的方法利用MAX-HEAPIFY把一个大小为n的数组转化为最大堆。每个叶结点都可以看成是只包含一个元素的堆。过程BUILD-MAX-HEAP堆树中其他节点都调用一次MAX-HEAPIFY。

BUILD-MAX-HEAP(A )

A.heap-size = A.length

for i = ⌊A.length/2⌋ downto 1  //从第一个非叶结点开始调整,直到根节点

MAX-HEAPIFY(A,i)







4.堆排序算法

初始时候,堆排序算法利用BUILD-MAX-HEAP将输入数组建成最大堆。因为数组中的最大元素总在根节点A[1]中,通过把它与A
进行交换,我们可以让该元素放到正确的位置。这时候我们从堆中去掉结点n,剩余的结点中,原来根节点的孩子仍然是最大堆,而新的节点可能会违背最大堆的性质。为了维护最大堆的性质,我们要做的仍然是调用MAX-HEAPIFY(A,1)。堆排序算法会不断重复这一过程,直到堆的大小从n-1降到2.

HEAPSORT(A)    //时间复杂度为O(nlgn)

BUILD-MAX-HEAP(A)

for i = A.length downto 2        //n-1次调用<font size="3"><font size="3"><font size="3"><font size="3"><font size="3"><font size="3"><font size="3"><font size="3"><font size="3"><font size="3"><font size="3"><font size="3"><font size="3"><font size="3"><font size="3"><font size="3"><font size="3"><font size="3"><font size="3"><font size="3"><font size="3"><font size="3"><font size="3"><font size="3"><font size="3"><font size="3"><font size="3"><span style="font-size:14px;"><span style="font-size:14px;"><span style="font-size:14px;">MAX-HEAPIFY</span></span></span></font></font></font></font></font></font></font></font></font></font></font></font></font></font></font></font></font></font></font></font></font></font></font></font></font></font></font>

exchange A[1] with A[i]

A.heap-size = A.heap-size - 1

MAX-HEAPIFY(A,1)                //时间复杂度为O(lgn)










以下是堆排序的完整实现:

#include <stdio.h>
#include <stdlib.h>
#include <malloc.h>
#include <time.h>

void PrintArr(int *pnArr, int nLen)
{
for (int i = 0; i < nLen; i++)
{
printf("%d ", pnArr[i]);
}
printf("\n");
}

//返回i父节点下标
int Parent(int i)                 //下标从0开始
{
return (i - 1) / 2;
}

//返回i左孩子下标
int LeftChild(int i)
{
return i * 2 + 1;
}

//返回i右孩子下标
int RightChild(int i)
{
return i * 2 + 2;
}

void Swap(int *a, int *b)
{
int nTmp = *a;
*a = *b;
*b = nTmp;
}
void MaxHeapify(int *pnArr, int nLen, int i)
{
int LChild = LeftChild(i);
int RChild = RightChild(i);
int nMaxPos;
if (LChild < nLen && pnArr[LChild] > pnArr[i])
{
nMaxPos = LChild;
}
else
{
nMaxPos = i;
}
if (RChild < nLen && pnArr[RChild] > pnArr[nMaxPos])
{
nMaxPos = RChild;
}

if (nMaxPos != i)
{
Swap(&pnArr[nMaxPos], &pnArr[i]);
MaxHeapify(pnArr, nLen,nMaxPos);
}

}
void BuildMaxHeap(int *pnArr, int nLen)
{
for (int i = Parent(nLen -1); i >= 0; i--)
{
MaxHeapify(pnArr, nLen, i);
}
}

void HeapSort(int *pnArr, int nLen)
{
BuildMaxHeap(pnArr, nLen);
for (int i = nLen - 1; i > 0; i--)
{
Swap(&pnArr[i], &pnArr[0]);
nLen--;
MaxHeapify(pnArr, nLen, 0);
}
}
int main()
{
int nArr[10] = {4,1,3,2,16,9,10,14,8,7};  //下标从0开始

PrintArr(nArr, 10);
HeapSort(nArr, 10);

PrintArr(nArr, 10);

system("pause");
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: