堆排序的基本概念
参考文档连接
http://blog.csdn.net/kimylrong/article/details/17150475
http://jingyan.baidu.com/article/5225f26b057d5de6fa0908f3.html
堆排序(Heapsort)是指利用堆积树(堆)这种数据结构所设计的一种排序算法,它是选择排序的一种。可以利用数组的特点快速定位指定索引的元素。堆分为大根堆和小根堆,是完全二叉树。大根堆的要求是每个节点的值都不大于其父节点的值,即A[PARENT[i]] >= A[i]。在数组的非降序排序中,需要使用的就是大根堆,因为根据大根堆的要求可知,最大的值一定在堆顶。
百度经验简
注意
特点
-
利用大顶堆(小顶堆)堆顶记录的是最大关键字(最小关键字)这一特性,使得每次从无序中选择最大记录(最小记录)变得简单。
其基本思想为(大顶堆):
1)将初始待排序关键字序列(R1,R2....Rn)构建成大顶堆,此堆为初始的无序区;
2)将堆顶元素R[1]与最后一个元素R[n]交换,此时得到新的无序区(R1,R2,......Rn-1)和新的有序区(Rn),且满足R[1,2...n-1]<=R[n];
3)由于交换后新的堆顶R[1]可能违反堆的性质,因此需要对当前无序区(R1,R2,......Rn-1)调整为新堆,然后再次将R[1]与无序区最后一个元素交换,得到新的无序区(R1,R2....Rn-2)和新的有序区(Rn-1,Rn)。不断重复此过程直到有序区的元素个数为n-1,则整个排序过程完成。
操作过程如下:
1)初始化堆:将R[1..n]构造为堆;
2)将当前无序区的堆顶元素R[1]同该区间的最后一个记录交换,然后将新的无序区调整为新的堆。
因此对于堆排序,最重要的两个操作就是构造初始堆和调整堆,其实构造初始堆事实上也是调整堆的过程,只不过构造初始堆是对所有的非叶节点都进行调整。
下面举例说明:
给定一个整形数组a[]={16,7,3,20,17,8},对其进行堆排序。
首先根据该数组元素构建一个完全二叉树,得到
这样整个区间便已经有序了。
从上述过程可知,堆排序其实也是一种选择排序,是一种树形选择排序。只不过直接选择排序中,为了从R[1...n]中选择最大记录,需比较n-1次,然后从R[1...n-2]中选择最大记录需比较n-2次。事实上这n-2次比较中有很多已经在前面的n-1次比较中已经做过,而树形选择排序恰好利用树形的特点保存了部分前面的比较结果,因此可以减少比较次数。对于n个关键字序列,最坏情况下每个节点需比较log2(n)次,因此其最坏情况下时间复杂度为nlogn。堆排序为不稳定排序,不适合记录较少的排序。
测试程序
4、源码实现用java数组
package jsu.java.sort;
/**
*
* @author Administrator
*
*数组实现java堆排序
*
*堆是完全二叉树
*也是平衡二叉树、也是二叉排序树。
*
*实现步骤
*1、构建 最大堆: 最大堆的任意子树根节点不小于任意子结点
*2、从最后一项开始,依次与根节点交换位置。
*3、由于步骤2的的交换可能破环了最大堆的性质,第0不再是最大元素,
*需要调用maxHeap调整堆(沉降法),将最大值调到最后面去。一次for循环所有最大值都在最后面,因此就是有序的。
*/
public class HeadSort {
public static void main(String[] args) {
int[] array =new int[]{43,22,44,32,54,65,66,76,444,45,445};
System.out.println("排序前");
ArrayUtils.print(array);
//实现对排序
System.out.println();
System.out.println("排序后");
headSort(array);
ArrayUtils.print(array);
}
private static void headSort(int[] array) {
/**
* 1、构建最大堆
* 性质:堆是一种重要的数据结构,为一棵完全二叉树, 底层如果用数组存储数据的话,
* 假设某个元素为序号为i(Java数组从0开始,i为0到n-1),如果它有左子树,那么左子树的位置是2i+1,
* 如果有右子树,右子树的位置是2i+2,如果有父节点,父节点的位置是(n-1)/2取整
*/
buildMaxHead(array);
}
private static void buildMaxHead(int[] array) {
// TODO Auto-generated method stub
int len=array.length/2;
for(int i=len-1;i>=0;i--){
maxHead(array,array.length,i);
}
/**
*2、从最后一项开始,依次与根节点交换位置。
*3、由于步骤2的的交换可能破环了最大堆的性质,第0不再是最大元素,
*需要调用maxHeap调整堆(沉降法),将最大值调到最后面去。一次for循环所有最大值都在最后面,因此就是有序的。
*/
for(int i=array.length-1;i>=0;i--){
ArrayUtils.changePosition(array, 0, i);
//这是最关键的一步,这里的长度一直在减,及不包含当前交换位置的最后一个元素
maxHead(array, i, 0);
}
}
/**
*假设某个元素为序号为i(Java数组从0开始,i为0到n-1),如果它有左子树,那么左子树的位置是2i+1,
* 如果有右子树,右子树的位置是2i+2,如果有父节点,父节点的位置是(n-1)/2取整
*@author nike
*/
private static void maxHead(int[] array, int length, int node) {
int left=node*2+1;
int right=node*2+2;
if(left<length&& array[node]<array[left]){
ArrayUtils.changePosition(array, node, left);
maxHead(array, length, left);
}
if(right<length&&array[node]<array[right]){
ArrayUtils.changePosition(array,node,right);
//一旦交换位置,可能破坏子节点的堆序列,所以递归调用重新排序。
maxHead(array, length, right);
}
}
}
class ArrayUtils{
public static void print(int[] array) {
// TODO Auto-generated method stub
for(int i=0;i<array.length;i++){
System.out.print(array[i]+" ");
}
}
public static void changePosition(int[] array, int node1, int node2) {
// TODO Auto-generated method stub
int temp=array[node1];
array[node1]=array[node2];
array[node2]=temp;
}
}