排序算法系列之堆排序

   堆排序(Heapsort)是指利用堆这种数据结构所设计的一种排序算法。堆积是一个近似完全二叉树的结构,并同时满足堆积的性质:即子结点的键值或索引总是小于(或者大于)它的父节点。

1 堆排序定义

     n个关键字序列Kl,K2,…,Kn称为堆,当且仅当该序列满足如下性质(简称为堆性质):
     (1) ki≤K2i且ki≤K2i+1 或(2)Ki≥K2i且ki≥K2i+1(1≤i≤  )
     若将此序列所存储的向量R[1..n]看做是一棵完全二叉树的存储结构,则堆实质上是满足如下性质的完全二叉树:树中任一非叶结点的关键字均不大于(或不小于)其左右孩子(若存在)结点的关键字。

    下图来自《算法导论》中堆排序一节:放到这里帮助大家更形象的理解堆。

图(a)是一个最大堆,图(b)是最大堆的数组表示。

2 堆节点的访问

通常堆是通过一维数组来实现的。在起始数组为 1 的情形中:

  • 父节点i的左子节点在位置
  • LEFT(i) return (2*i);
  • 父节点i的右子节点在位置 
  • RIGHT(i) return (2*i+1);
  • 子节点i的父节点在位置 
  • PARENT(i) return floor((i/2));

3 堆的操作

    我们以最大堆排序来说明排序过程。

   在堆的数据结构中,堆中的最大值总是位于根节点。堆中定义以下几种操作:

  • 最大堆调整(MaxHeapify):将堆的末端子节点作调整,使得子节点永远小于父节点
  • 创建最大堆(BuildMaxHeap):将堆所有数据重新排序
  • 堆排序(HeapSort):移除位在第一个数据的根节点,并做最大堆调整的递归运算

4 堆排序特点

     堆排序(HeapSort)是一树形选择排序。
     堆排序的特点是:在排序过程中,将R[l..n]看成是一棵完全二叉树的顺序存储结构,利用完全二叉树中双亲结点和孩子结点之间的内在关系,在当前无序区中选择关键字最大(或最小)的记录。

5 堆排序

  算法动画演示

    大根堆排序实例:对于关键字序列(42,13,24,91,23,16,05,88),在建堆过程中完全二叉树及其存储结构的变化情况。

动画演示:【动画演示

 堆排序c/c++实现 

     堆排序利用了大根堆(或小根堆)堆顶记录的关键字最大(或最小)这一特征,使得在当前无序区中选取最大(或最小)关键字的记录变得简单。

    堆操作的实现:

(1)最大堆调整MaxHeapify(A,i);

// 使以i为根的子树成为最大堆
void MaxHeapify(int arr[],int len,int i)
{
	int l = 2*i,r = 2*i+1;
	int largest;
	if(l<=len && arr[l]>arr[i])
	    largest = l;
	else
	    largest = i;
	if(r<=len && arr[r]>arr[largest])
	    largest = r;
	if(largest != i)
	{
		int temp = arr[i];
		arr[i] = arr[largest];
		arr[largest] = temp;
		MaxHeapify(arr,len,largest);
	}
}

(2)创建最大堆BuildMaxHeap(A)

void BuildMaxHeap(int arr[],int len)
{
	for(int i=len/2;i>=1;i--)
	{
	    MaxHeapify(arr,len,i);
	}
	cout<<"build heap is ok!"<<endl;
}

(3)堆排序HeapSort(A)

void HeapSort(int arr[],int len)
{
	BuildMaxHeap(arr,len);
	printvalue(arr,len);
	for(int i=len;i>=2;i--)
	{
	    int temp = arr[1];
            arr[1] = arr[i];
	    arr[i] = temp;
	    len--;
	    MaxHeapify(arr,len,1);
	}
}

6 算法分析

     堆排序的时间,主要由建立初始堆和反复重建堆这两部分的时间开销构成,它们均是通过调用最大堆调整MaxHeapify(A,i)实现的。
     堆排序的最坏时间复杂度为O(nlgn)。堆排序的平均性能较接近于最坏性能。
     由于建初始堆所需的比较次数较多,所以堆排序不适宜于记录数较少的文件。
     堆排序是就地排序,辅助空间为O(1),
     它是不稳定的排序方法。

7 堆排序与直接插入排序的区别

     直接选择排序中,为了从R[1..n]中选出关键字最小的记录,必须进行n-1次比较,然后在R[2..n]中选出关键字最小的记录,又需要做n-2次比较。事实上,后面的n-2次比较中,有许多比较可能在前面的n-1次比较中已经做过,但由于前一趟排序时未保留这些比较结果,所以后一趟排序时又重复执行了这些比较操作。
     堆排序可通过树形结构保存部分比较结果,可减少比较次数。

8 参考文章

  1.堆排序:http://student.zjzk.cn/course_ware/data_structure/web/paixu/paixu8.4.2.3.htm

  2.堆排序基础讲解:http://www.wutianqi.com/?p=1820

  3.算法导论学习总结:http://www.wutianqi.com/?p=2343

  4.堆排序:http://zh.wikipedia.org/wiki/%E5%A0%86%E6%8E%92%E5%BA%8F

9 整体代码(包括实验程序C++)

#include <cstdlib>
#include <iostream>
using namespace std;

void printvalue(int arr[],int len)
{
	int i;
	cout<<"the arr value:";
	for(i=1;i<=len;i++)
	{
		cout<<arr[i]<<" ";
	}
	cout<<endl;
}
void MaxHeapify(int arr[],int len,int i)
{
	int l = 2*i,r = 2*i+1;
	int largest;
	if(l<=len && arr[l]>arr[i])
	{
		largest = l;
	}
	else
	{
		largest = i;
	}
	if(r<=len && arr[r]>arr[largest])
	{
		largest = r;
	}
	if(largest != i)
	{
		int temp = arr[i];
		arr[i] = arr[largest];
		arr[largest] = temp;
		MaxHeapify(arr,len,largest);
	}
}

void BuildMaxHeap(int arr[],int len)
{
	for(int i=len/2;i>=1;i--)
	{
		MaxHeapify(arr,len,i);
	}
	cout<<"build heap is ok!"<<endl;
}
void HeapSort(int arr[],int len)
{
	BuildMaxHeap(arr,len);
	printvalue(arr,len);
	for(int i=len;i>=2;i--)
	{
		int temp = arr[1];
		arr[1] = arr[i];
		arr[i] = temp;
		len--;
		MaxHeapify(arr,len,1);
	}
}
int main(int argc, char *argv[])
{
	int a[100];

	int len =88;
	for(int i=1;i<=len;i++)  //88个数
	{
		a[i] = len - i + 1;
	}
	cout<<"init a[]:";
	printvalue(a,len);
	cout<<endl;

	HeapSort(a,len);
	cout<<"After HeapSort!"<<endl;
	printvalue(a,len);

    system("PAUSE");
    return EXIT_SUCCESS;
}
时间: 2024-09-07 01:38:10

排序算法系列之堆排序的相关文章

排序算法系列之二叉查找树

排序算法系列之二叉查找树 基本概念   二叉查找树(Binary Search Tree),或者是一棵空树,或者是具有下列性质的二叉树: 若它的左子树不空,则左子树上所有结点的值均小于它的根结点的值: 若它的右子树不空,则右子树上所有结点的值均大于它的根结点的值: 它的左.右子树也分别为二叉排序树.                                             序列:1 3 4 6 7 8 10 13 14                 序列:2 3 4 6 7 9

Java排序算法&amp;amp;nbsp;堆排序

1991年计算机先驱奖获得者.斯坦福大学计算机科学系教授罗伯特·弗洛伊德(Robert W.Floyd)和威廉姆斯(J.Williams)在1964年共同发明了著名的堆排序算法( Heap Sort ).本文主要介绍堆排序用Java来实现. AD: 堆积排序(Heapsort)是指利用堆积树(堆)这种资料结构所设计的一种排序算法,可以利用数组的特点快速定位指定索引的元素.堆排序是不稳定的排序方法,辅助空间为O(1), 最坏时间复杂度为O(nlog2n) ,堆排序的堆序的平均性能较接近于最坏性能.

排序算法系列之希尔排序

算法排序之希尔排序 希尔排序,也称递减增量排序算法,是插入排序的一种高速而稳定的改进版本.  基本思想    先取一个小于n的整数d1作为第一个增量,把文件的全部记录分成d1个组.所有距离为dl的倍数的记录放在同一个组中.先在各组内进行直接插人排序:然后,取第二个增量d2<d1重复上述的分组和排序,直至所取的增量dt=1(dt<dt-l<-<d2<d1),即所有记录放在同一组中进行直接插入排序为止. 希尔排序的时间性能优于直接插入排序的原因: ①当文件初态基本有序时直接插入排

常用内部排序算法之三:堆排序

前言 堆排序是以堆为原型的排序.堆首先是一棵二叉树,具有以下两个性质:每个节点的值大于或者等于其左右孩子结点的值,称为大顶堆:或者每个节点的值都小于或者等于其左右孩子结点的值,称为小顶堆.从这个定义中可以发现,堆得根节点要么是最大值要么是最小值.实现堆排序的基本思想是:将待排序的序列构造成一个大顶堆或者小顶堆.此时整个堆满足根节点是最大值或者最小值.将根节点移走,并与堆数组的最后一个值进行交换,这样的话最后一个值就是最大值或者最小值了.然后将剩余n-1(假设原来总共有n个元素)未排序的序列重新构

排序算法系列之合并排序

       归并排序(Merge sort,合并排序)是建立在归并操作上的一种有效的排序算法.该算法是采用分治法(Divide and Conquer)的一个非常典型的应用. 一 归并操作 基本思想     归并操作(merge),指的是将两个已经排序的序列合并成一个序列的操作,归并排序算法依赖归并操作. 归并操作的过程如下: 申请空间,使其大小为两个已经排序序列之和,该空间用来存放合并后的序列 设定两个指针,最初位置分别为两个已经排序序列的起始位置 比较两个指针所指向的元素,选择相对小的元素

排序算法系列之选择排序

简单选择排序也是直接选择排序 基本思想     选择排序(Selection sort)是一种简单直观的排序算法.它的工作原理如下.首先在未排序序列中找到最小(大)元素,存放到排序序列的起始位置,然后,再从剩余未排序元素中继续寻找最小(大)元素,然后放到已排序序列的末尾.以此类推,直到所有元素均排序完毕. 排序过程:     对于一组关键字{K1,K2,-,Kn}, 首先从K1,K2,-,Kn中选择最小值,假如它是 Kz,则将Kz与 K1对换:然后从K2,K3,- ,Kn中选择最小值 Kz,再将

[算法系列之一]堆排序

前序: (二叉)堆数据结构是一种数组对象,它可以被视为一棵完全二叉树.树中每个节点与数组中存放该节点值的那个元素对应. 树的每一层都是填满的,最后一层除外. 树的根为a[1] (在这里是从1开始的,也可以从0开始),给定了某个节点的下标i,其父节点为i/2,左二子为2*i,右儿子为2*i+1. 二叉堆满足二个特性: 1.父结点的键值总是大于或等于(小于或等于)任何一个子节点的键值. 2.每个结点的左子树和右子树都是一个二叉堆(最大堆或最小堆). 当父结点的键值总是大于或等于任何一个子节点的键值时

排序算法系列之冒泡排序

    交换排序的基本思想是:两两比较待排序记录的关键字,发现两个记录的次序相反时即进行交换,直到没有反序的记录为止.应用交换排序基本思想的主要排序方法有:冒泡排序和快速排序. 基本思想 1.冒泡排序算法的过程: 比较相邻的元素.如果第一个比第二个大,就交换他们两个. 对每一对相邻元素作同样的工作,从开始第一对到结尾的最后一对.在这一点,最后的元素应该会是最大的数. 针对所有的元素重复以上的步骤,除了最后一个. 持续每次对越来越少的元素重复上面的步骤,直到没有任何一对数字需要比较. 2.以数组R

排序算法系列之插入排序

算法排序之插入排序 基本思想 一般来说,插入排序都采用in-place在数组上实现.具体算法描述如下: 从第一个元素开始,该元素可以认为已经被排序 取出下一个元素,在已经排序的元素序列中从后向前扫描 如果该元素(已排序)大于新元素,将该元素移到下一位置 重复步骤3,直到找到已排序的元素小于或者等于新元素的位置 将新元素插入到该位置后 重复步骤2~5 伪码描述: INSERTION-SORT(A) 1 for j = 2 to length[A] 2     do key = A[j] 3