java 实现对排序

堆排序的基本概念

参考文档连接

http://blog.csdn.net/kimylrong/article/details/17150475

http://jingyan.baidu.com/article/5225f26b057d5de6fa0908f3.html

堆排序(Heapsort)是指利用堆积树(堆)这种数据结构所设计的一种排序算法,它是选择排序的一种。可以利用数组的特点快速定位指定索引的元素。堆分为大根堆和小根堆,是完全二叉树。大根堆的要求是每个节点的值都不大于其父节点的值,即A[PARENT[i]] >= A[i]。在数组的非降序排序中,需要使用的就是大根堆,因为根据大根堆的要求可知,最大的值一定在堆顶。

百度经验简

堆排序利用了大根堆(或小根堆)堆顶记录的 关键字最大(或最小)这一特征,使得在当前无序区中选取最大(或最小)关键字的记录变得简单。
(1)用大根堆排序的基本思想
① 先将初始文件R[1..n]建成一个大根堆,此堆为初始的无序区
② 再将关键字最大的记录R[1](即堆顶)和无序区的最后一个记录R[n]交换,由此得到新的无序区R[1..n-1]和有序区R[n],且满足R[1..n-1].keys≤R[n].key
③由于交换后新的根R[1]可能违反堆性质,故应将当前无序区R[1..n-1]调整为堆。然后再次将R[1..n-1]中关键字最大的记录R[1]和该区间的最
后一个记录R[n-1]交换,由此得到新的无序区R[1..n-2]和有序区R[n-1..n],且仍满足关系R[1..n-2].keys≤R[n-1..n].keys,同样要将R[1..n-2]调整
为堆。
……
直到无序区只有一个元素为止。
(2)大根堆排序算法的基本操作:
①建堆,建堆是不断调整堆的过程,从len/2处开始调整,一直到第一个节点,此处len是堆中元素的个数。建堆的过程是线性的过程,从len/2到0处一直调用调整堆的过程,相当于o(h1)+o(h2)…+o(hlen/2) 其中h表示节点的深度,len/2表示节点的个数,这是一个求和的过程,结果是线性的O(n)。
②调整堆:调整堆在构建堆的过程中会用到,而且在堆排序过程中也会用到。利用的思想是比较节点i和它的孩子节点left(i),right(i),选出三者最大(或者最小)者,如果最大(小)值不是节点i而是它的一个孩子节点,那边交互节点i和该节点,然后再调用调整堆过程,这是一个递归的过程。调整堆的过程时间复杂度与堆的深度有关系,是lgn的操作,因为是沿着深度方向进行调整的。
③堆排序:堆排序是利用上面的两个过程来进行的。首先是根据元素构建堆。然后将堆的根节点取出(一般是与最后一个节点进行交换),将前面len-1个节点继续进行堆调整的过程,然后再将根节点取出,这样一直到所有节点都取出。堆排序过程的时间复杂度是O(nlgn)。因为建堆的时间复杂度是O(n)(调用一次);调整堆的时间复杂度是lgn,调用了n-1次,所以堆排序的时间复杂度是O(nlgn) [2]  

注意

①只需做n-1趟排序,选出较大的n-1个 关键字即可以使得文件递增有序。
②用小根堆排序与利用大根堆类似,只不过其排序结果是递减有序的。堆排序和直接 选择排序相反:在任何时刻堆排序中无序区总是在有序区之前,且有序区是在原向量的尾部由后往前逐步扩大至整个向量为止

特点

堆排序(HeapSort)是一树形 选择排序。堆排序的特点是:在排序过程中,将R[l..n]看成是一棵 完全二叉树顺序存储结构,利用完全二叉树中双亲结点和孩子结点之间的内在关系(参见二叉树的顺序存储结构),在当前无序区中选择 关键字最大(或最小)的记录
3、图解操作过程
  1.    利用大顶堆(小顶堆)堆顶记录的是最大关键字(最小关键字)这一特性,使得每次从无序中选择最大记录(最小记录)变得简单。

        其基本思想为(大顶堆):

        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;
	}
	
}



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值