堆排序

来源:互联网 发布:耐药监测数据处理软件 编辑:程序博客网 时间:2024/04/30 23:39

 

堆排序快速排序归并排序一样都是时间复杂度为O(N*logN)的几种常见排序方法。学习堆排序前,先讲解下什么是数据结构中的二叉堆。

堆排序是利用堆的特性对记录序列进行排序的一种排序方法。

即小顶堆:父结点的值小于左右孩子结点的值,大顶堆的相反



假设我们要排序的序列是{50,10,90,30,70,40,80,60,20}

堆排序分为两个步骤:

1.构造大顶堆(或者小顶堆)

如图所示:将输入的数组不断调整,使得其构成大顶堆


void HeapAjust(int* arr,int start,int end){int temp,j;temp = arr[start];for (j = 2*s;j <= end;j*=2){if(j < m && arr[j] < arr[j+1])//左孩子小于右孩子j++;if(temp >= arr[j])break;arr[start] = arr[j];    start = j;}arr[start] = temp;}


在这里我们使用大顶堆,依次输出元素如下图:

依次类推输出所有元素


程序如下:

void HeapSort(int* arr,int len){int i;for(i = len/2;i>= 0;i--)//构造大顶堆HeapAjust(arr,i,len-1);for (i = len-1;i > 0;i--){swap(arr[0],arr[i]);//取堆顶的记录和当前未经排序子序列的最后一个记录交换HeapAjust(arr,1,i-1);}}

这里给出完整代码(仅供参考):

#include <stdio.h>#include <stdlib.h>void swap(int& data1,int &data2){int temp = data1;data1 = data2;data2 = temp;}//返回i的父结点int parent(int i){return (i-1)/2;}//返回i的左孩子结点int leftchild(int i){return 2*i+1;}//返回i的右孩子结点int rightchild(int i){return 2*i+2;}void MaxHeapify(int *arr,int len,int i){int lchild = leftchild(i);int rchild = rightchild(i);int nmax;if(lchild < len && arr[lchild] > arr[i])nmax = lchild;elsenmax = i;if(rchild < len && arr[rchild] > arr[nmax])nmax = rchild;if (nmax != i){swap(arr[nmax],arr[i]);MaxHeapify(arr,len,nmax);}}//堆排序void HeapSort(int* arr,int len){   for (int i = parent(len - 1);i >= 0;i--)       MaxHeapify(arr,len,i);      for (int j = len -1;j > 0;j--)   {   swap(arr[j],arr[0]);   len--;   MaxHeapify(arr,len,0);   }}void print(int* arr,int len){int i =0;while (i < len)printf("%d ",arr[i++]);// for (int i = 0;i < len;i++)// {// printf("%d ",arr[i]);// }}int main(){    int nArr[10] = {4,1,3,2,16,9,10,14,8,7};    printf("排序前:");    print(nArr, 10);    HeapSort(nArr, 10);printf("\n排序后:");    print(nArr, 10);system("pause");    return 0;}


复杂度分析:

运行时间主要是消耗在初始建堆和重建堆时的反复筛选上,在构建堆的过程中,因为是完全二叉树最下层最右边的非终端结点开始构建,将它与其孩子比较若有必要进行交换,对于每个非终端结点来说,其实最多进行两次比较和呼唤操作,因此整个构建过程的时间复杂度为O(n),在排序时,第i次取堆顶记录重建时需要使用O(logi)的时间,并需要取 n-1 次堆顶记录,因此重建堆时的时间复杂度为 O(nlogn).因此总体来说堆排序的时间复杂度为  O(nlogn)。由于堆排序对原始记录的排序状态并不敏感,因此他无论是最好、最坏和平均时间复杂度均为  O(nlogn)。这在性能是要超过 冒泡排序、简单选择排序、直接插入排序的 O(n^2) 的时间复杂度。空间复杂度上,他只有一个用来交换的暂存单元,也非常不错。但是由于记录的比较与交换是跳跃式进行,因此堆排序是一种不稳定的排序方法。





1 0
原创粉丝点击