堆排序详解

堆排序

1. 堆定义

堆是一种特殊的二叉树,具备以下两种性质

1)每个节点的值都大于(或小于,称为最小堆)其子节点的值

2)树是完全平衡的,并且最后一层的树叶都在最左边

二叉堆是一种完全二叉树,其任意子树的左右节点(如果有的话)的键值一定比根节点大,下图是一个最小堆。

二叉堆的表示结构:

由于二叉堆的结构性质,可以采用一维数组来表示二叉堆。

二叉堆元素从数组下标1开始,则

T[i]的左儿子为T[2*i],右儿子为T[2*i+1]

T[i]的父结点为T[i/2]

《堆排序详解》

2.堆的操作

2.1 插入

将待插入的元素放在数组的最后位置,这可能破坏了二叉堆结构性质,通过“上浮”操作维护其结构。

2.2 删除

将数组的第一位元素删除,并将数组最后位置的元素放到第一位,这可能破坏其结构性质,可通过“下沉”操作维护二叉堆的结构。

上浮操作:

void swap(int* _a, int* _b)

{

int tmp =*_a;

*_a = *_b;

*_b = tmp;

}

/*

* 从底向上维护二叉堆中第n位元素的堆序性质

* 如果第n位元素的值大于其父结点n/2的值,则交换

* 直到根结点或满足父结点值小于当前节点值

**/

void shiftUp(int* T, int n)

{

assert(T !=NUU && n>=1);

int i = n;

int p =i/2;

while(i !=1)

{

p =i/2;

//不满足最小堆结构性质,交换当前结点与其父结点值

//指向当前结点的父结点,继续判断是否满足堆序性质

if(T[i]< T[p])

{

swap(&T[i],&T[p]);

i= p;

}else{

return;

}

}

}

下沉操作:

/*

* 自顶向下维护二叉堆的堆序性质

* 如果第i位元素没有子结点,则结束

* 如果第i位元素有子结点,则c为值较小子结点的下标

* 如果T[i]>T[c],则交换i和c位置的元素值,i=c,继续进行

**/

void shiftDown(int* T, int n)

{

assert(T !=NULL && n >= 1);

int i = 1;

int c;

do

{

c =2 * i;

if(c> n)

return;

//有两个子结点,找到值小的子结点

if((c+1)<= n)

{

if(T[c+1]< T[c])

c++;

}

if(T[i]> T[c])

{

swap(&T[i],&T[c]);

i= c;

}else{

return;

}

} while(c <= n);

}

3.堆排序

有了二叉堆及其操作,可以据其定义堆排序算法。

未排序堆

已排序部分

1 i n

堆排序主要包括两个部分:

a.建立堆

通过循环的shiftUp操作,将原数组构造成堆结构;

b.排序

数组分成两部分,未排序的堆和已排序部分。初始时,已排序部分为空。

此时T[1]为前i个元素中最小的,将T[1]和T[i]交换,这样就把剩下数组中最小的元素放到已排序部分;再通过shiftDown操作维护堆序性质;i值从n到2循环

void heapSort(int* T, int len)

{

assert(T !=NULL && len >= 0);

int i;

for(i=2;i<=len; ++i)

{

shiftUp(T,i);

}

for(i=len; i>=2; –i)

{

swap(&T[i],&T[1]);

shiftDown(T,i-1);

}

}

int main()

{

int a[] = {-1,8,4,2,5,3,7,6,9,1};

printf(“before heapSort:\n”);

int i, len = 9;

for(i=0; i<len+1; i++)

printf(“%d “,a[i]);

printf(“\n”);

heapSort(a, len);

printf(“after heapSort:\n”);

for(i=0; i<len+1; i++)

printf(“%d “,a[i]);

printf(“\n”);

return 0;

}

点赞