首页 > 其他 > 详细

阿布学排序之堆排序

时间:2014-05-15 23:48:40      阅读:570      评论:0      收藏:0      [点我收藏+]
/**
 * 需求:堆排序的实现
 * 知识储备:
 * 满二叉树:除叶子结点外的所有结点均有两个子结点,所有叶子结点必须在同一层上。
 * 完全二叉树:
 * 若二叉树的深度为h,除第h层外,其它各层(1~h-1)的节点数都达到最大个数,第h层所有结点都连续集中在最左边。
 * 完全二叉树是有满二叉树而引出来的,对于深度为K的,有N个结点的二叉树,当且仅当每一个结点都与深度为K的满二叉树中编号从1至n的结点一一对应时称它为完全二叉树。
 * 
 * 二叉堆是完全二叉树或者是近似完全二叉树。
 * 二叉堆特性:
 * 1、父结点的键值总是大于或等于(小于或等于)任何一个子结点的键值
 * 2、每个结点的左子树和右子树都是一个二叉堆(都是最大堆或者最小堆)
 * 最大堆:父结点的键值总是大于或等于任何一个子结点的键值。
 * 最小值:父结点的键值总是小雨或等于任何一个子结点的键值。
 * 
 * 堆的存储特性:
 * 一般用数组存储,i结点的父结点的下标是(i- 1)/ 2,它的左右结点下标为2 * i + 1, 2 * i + 2。如第0个结点左右结点下标为1和2。
 * (a)逻辑结构:(按层编号)
 * 			10
 * 		  /     * 		15		56
 * 	   /  \     /
 * 	  25   30   70
 * (b)存储结构
 * 	10 15 56 25 30 70
 * 
 * 常见的堆操作:
 * 1、建立堆:
 * 数组具有对应的树表示形式,一般情况下,树并不满足堆的条件,通过重新排列元素,可以一棵“堆化树”,如下图建立一个最小堆
 * 初始表:40 10 30 									堆化树:10 40 30
 * 			A[0]											A[0]
 * 			40												10	
 * 		  /	  \											   /    * 		10     30			 ----->						40      30	
 * 		A[1]   A[2]										A[1]    A[2]
 * 2、插入一个元素:
 * 新元素加入到表中,随后树被更新为最小堆次序,例如下图将15加入到表中
 * 插入后的表:10 40 30 15
 * 			15加入到A[3]									重新树的排序
 * 				10											10
 * 			   /  \										   /    * 			 40    30  		 ----->       				 15		30		
 * 			/											 /	
 * 		   15											40
 * 3、删除一个元素:	
 * 删除总是发生在跟A[0]处,表中最后一个元素被用来填补空缺位置,结构树被更新以恢复堆的形式
 *  		删除位于A[0]的元素10							将40移到A[0]        重新恢复堆
 * 				空											40				15
 * 			   /  \										   /   \		   /   * 			 15    30  		 ----->       				 15		30		  40   30
 * 			/											 /	
 * 		   40											空
 * 思路:时间复杂度为O(N * logN)
 * 对于插入操作:
 * 每次插入都是将新数据放在数组最后,可以发现从这个新数据的父结点到根节点必然为一个有序的数列。
 * 1、对于插入的下标为i的结点,其父结点下标为(i - 1)/2
 * 2、进行比较,交换,最后恢复成最小堆
 * 对于删除操作:
 * 堆中每次删除数据都只能删除第0个数据,为了便于重建堆,实际的操作是将最后一个数据的值赋给跟结点,然后再从根节点开始进行一次从上而下的调整。
 * 调整时,先在左右孩子节点中找最小的,如果父结点比这个最小的子结点还小则不需要调整了,反之将父结点和它交换后再考虑结点,想当于从根节点
 * 将一个数据的下沉过程。
 * 1、从i结点开始调整,n为结点总数,从0开始计算,第i结点的子结点为2 * i + 1,2 * i + 2
 * @author AbuGe
 *
 */
public class HeapSort
{
	//在最小堆中的数据上升函数函数
	public  void minHeapFixup(int[] a, int i)
	{
		int j;
		j = (i - 1) / 2;//父结点下标
		int tmp = a[i];
		while(j >= 0 && i != 0)
		{
			if(a[i] >= a[j])
				break;
			//a[i] = a[j];//把较大的结点往下移动,替换它的子结点
			//a[j] = tmp;
			swap(a, i, j);
			i = j;
			j = (i - 1) / 2;
		}
	}
	
	//在最小堆中的数据下降函数
	public void minHeapFixdown(int[] a, int i,  int n)
	{
		int j;
		//这一句很重要
		int tmp = a[i];
		j = 2 * i + 1;//左结点坐标
		while(j < n)
		{
			//在左右孩子中找最小的
			if(j + 1 < n && a[j + 1] < a[j])
				j++;
			
			if(tmp <= a[j])
				break;
			
			swap(a, i, j);
			//a[i] = a[j];
			//a[j] = tmp;
			
			i = j;
			j = 2 * i + 1;
		}
	}
	
	//建立最小堆
	public void makeMinHeap(int[] a, int n)
	{
		for(int i = n / 2 - 1; i >= 0; i--)
		{
			minHeapFixdown(a, i, n);
		}
	}
	
	//最小堆中添加元素
	public void minHeapAdd(int[] a, int n, int nNum)
	{
		a[n] = nNum;
		minHeapFixup(a, n);
	}
	
	//最小堆中删除元素
	public void minHeapDelete(int a[], int n)
	{
		swap(a, 0, n - 1);
		minHeapFixdown(a, 0, n - 1);
	}
	
	//交换数据
	public void swap(int[] a, int x, int y)
	{
		int tmp = a[x];
		a[x] = a[y];
		a[y] = tmp;
	}
	/**
	 * 注意使用最小堆排序后是递减数组,要得到递增数组,用最大堆,由于每次重新恢复堆的时间复杂度为O(logN),共n-1次重新恢复操作,
	 * 再加上前面建立堆时N/2次向下调整,,每次调整的时间复杂度为O(logN),两次操作的时间复杂度相加还是O(N*logN),故堆排序的时间复杂
	 * 度为O(NlogN)
	 */
	//最小堆-->降序排序
	/**
	 * 首先堆建立好之后第0个数据是堆中最小的数据,取出这个数据再执行minHeapFixdown的删除操作。这样执行后第0个数据又是堆中最小的
	 * 数据,重复操作直至堆中只剩一个数据时就得到一个降序的数组。
	 * @param a
	 * @param n
	 */
	public void minHeapToDescendSort(int a[], int n)
	{
		for(int i = n - 1; i >= 1; i--)
		{
			swap(a, i, 0);
			minHeapFixdown(a, 0, i);
		}
	}
	public static void main(String[] args)
	{
		int[] a = {9, 12, 17, 30, 50, 20, 60, 65, 4, 19};
		int len = a.length;
		HeapSort hs = new HeapSort();
		hs.makeMinHeap(a, len);
		
		for(int sort : a)
			System.out.print(sort + "\t");
		
		System.out.println();
		hs.minHeapToDescendSort(a, len);
		
		for(int sort : a)
			System.out.print(sort + "\t");
	}
}

阿布学排序之堆排序,布布扣,bubuko.com

阿布学排序之堆排序

原文:http://blog.csdn.net/hxysea/article/details/25901743

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!