堆排序原理及c语言实现

来源:互联网 发布:穷二代的出路知乎 编辑:程序博客网 时间:2024/06/05 10:26

很多同学在开始学排序算法的时候都会觉得堆排序是比较难的,其实对于堆排序,只要理解了两点,应该就可以掌握堆排序了,下面说说要理解哪两点以及c语言的实现。

第一点:堆的特性。

下面的堆的特性定义来自算法导论:

最大堆特性是指除了跟以外的每个结点i,有A[PARENT[i]]>=A[i],即每个结点的值至多是和其父结点的值一样大。

最小堆特性是指出了跟以外的每个结点i,有A[PARENT[i]]<=A[i],即每个结点的值至少是和其父结点的值一样大。

也就是说,最大堆的父结点都大于等于其子结点,最小堆的父结点都小于等于其子结点。

第二点:数组下标和堆结点的关系。

对于有n个元素的数组a[n],其下标和堆结点的关系为:

父结点a[parent]的左右子结点分别是a[parent*2+1],a[parent*2+2], 0<= parent <= n/2-1;

数组下标从大到小看,下标为n/2-1的结点为堆的第一个非叶子结点。

假设我们现在有一个数组a[10] = {4,1,3,2,16,9,10,14,8,7},对应的二叉树为:


树结点外的数字就是该结点对应的数组下标。

跟结点4的下标为0,其左子结点1的下标为1,1 = 0 * 2 + 1;

其右子结点3的下标为2,2 = 0 * 2 + 2;

1的左子结点2的下标,3 = 1 * 2  + 1;右子结点16的下标 4 = 1 * 2 + 2;

3的左子结点9的下标,5 = 2 * 2 + 1; 右子结点10的下标 6 = 2 * 2 + 2;

2的左子结点14的下标,7 = 3 * 2 + 1;右子结点8的下标 8 = 3 * 2 + 2;

16的左子结点7的下标,9 = 4 * 2 + 1;

以最大堆为例,堆排序的过程就是将数组建立成一个最大堆,每次把跟结点取出来,就是最大的一个数,所以遍历完整个数组后,其算法复杂度就是O(n)*O(logn),其中O(n)是遍历数组的算法复杂度,O(logn)是调整堆的算法复杂度。

下面是c语言的实现:

void swap(int *a, int *b){int temp;temp = *a;*a = *b;*b = temp;}void buildMaxHeap(int a[], int parentIndex, int lastIndex){int left;int right;int max;left = parentIndex * 2 + 1;/* 左子结点下标 */right = parentIndex * 2 + 2;/* 右子结点下标 */while (left <= lastIndex){max = left;/* 先假设左子结点为最大 *//* 如果存在右子结点 */if (right <= lastIndex){if (a[left] < a[right]){max = right;/* 右子结点比左子结点要大 */}}/* 如果父结点比子结点要小,那么子结点上升为父结点,*  原先的父结点下降为子结点,继续重复调整 */if (a[parentIndex] < a[max]){swap(&a[parentIndex], &a[max]);parentIndex = max;}else{return;/* 父结点比子结点都大,符合最大堆的特性,返回 */}/* 继续往下调整 */left = parentIndex * 2 + 1;right = parentIndex * 2 + 2;}}/* n是数组元素的个数 */void heapSort(int a[], int n){int i;int unsort;/* 先将数组建立成一个最大堆*/for (i = n / 2 - 1; i >= 0; i--){/* 从最后一个非叶子结点开始调整 */buildMaxHeap(a, i, n - 1);}swap(&a[0], &a[n - 1]);/* 遍历数组,每次取一个最大的,然后调整堆 */for (unsort = n - 1; unsort > 0; unsort--){buildMaxHeap(a, 0, unsort-1);swap(&a[0], &a[unsort-1]);}}int main(){int i;int a[] = {4,1,3,2,16,9,10,14,8,7};heapSort(a, 10);for (i = 0; i < 10; i++){printf("%d ", a[i]);}printf("\n");return 0;}