算法之堆排序

堆排序

堆:如果一棵完全二叉树的每个节点都大于(小于)它的子节点称之为堆。

    k >= 2k + 1 && k >= 2k + 2

  或者 k <= 2k + 1 && k <= 2k + 2

堆分:大顶堆和小顶堆。

完全二叉树:除了叶子节点所有的其他节点都有完整的左子树和右子数,除了最后一层的非叶子节点以外。


使用堆排序分两步:

1 建立一个无序的堆。

2 输出对顶元素,然后用最后一个元素代替堆顶元素,是之再成为一个堆。


比如: 3,22,8,11,55,1这样一个数使用堆排序的过程如下:

1. 先按顺序建立一颗完全二叉树:



按层级顺序填充数据,从最后一个非叶子节点看,是否满足堆的要求。

先从8开始,不满足堆的要求,和1换位。(该节点都必须大于或者小于左右子节点),我们按小顶堆排序,所以节点都应小于左右子节点。


再从22开始,不满足要求,和11换位。(若该节点小于左右时,和较小的子节点互换)


再从3开始,不满足要求,和1互换位置。


这样就建成了堆。所有的节点都不大于它的子节点。


2 第二阶段。

  从初始的堆中输出堆顶元素,然后将最后一个元素放到堆顶,然后在进行堆的定义置换。

  我们把较大元素从顶上落到底层的过程叫做------- 筛选。


  先输出1,然后将8置于堆顶


 然后按照步骤1的方式将其筛选成符合条件的堆,知道8落地为止,即筛选8。

先和3替换,8落地。符合堆


输出3,将55放置堆顶


然后筛选55得到如下


输出8,,22置堆顶


筛选22,将11置堆顶,

输出11,输出22,输出55

排序后应该是1,3, 8,11, 22, 55


我们可以用一维数组表示这个完全二叉树。

假如当前节点为K:

     那么父节点为:  (k - 1) / 2

    左孩子为: 2k  + 1

   有孩子为:2k + 2


其Java代码实现如下:

package andy.test;

/** 
 * @author Zhang,Tianyou
 * @version 2014年11月7日 下午10:32:31
 */

public class HeapSortTest {

	public static void main(String[] args) {
		int[] a = {3,22,8,11,55,1};
		heapSort(a);

	}
	
	private static void heapOne(int[] a, int n, int k) {
		//节点k进行筛选
		//a: 堆数据  n:堆中有效数据个数   k:筛选节点
		
		int k1 = 2*k + 1;//左子树序号
		int k2 = 2*k + 2;//右子数序号
		if(k1 >= n && k2 >= n)return ; //已经是叶子节点
		
		int a1 = Integer.MAX_VALUE;
		int a2 = Integer.MAX_VALUE;
		if(k1 < n)a1 = a[k1]; //左孩子值
		if(k2 < n)a2 = a[k2]; //右孩子值
		
		if(a[k] <= a1 && a[k] <= a2)return; //已符合堆的要求
		
		//找到左右孩子中最小的,和它交换
		if(a1 < a2){
			int temp = a[k];
			a[k] = a[k1];
			a[k1] = temp;
			heapOne(a, n, k1); //继续筛选子树
		}else{
			int temp = a[k];
			a[k] = a[k2];
			a[k2] = temp;
			heapOne(a, n, k2); //继续筛选子树
		}
		
	}
	
	static void heapSort(int[] a){
		//建立初始堆 
		//a.length     最后一个叶子节点   
		//a.length/ 2      最后一个非叶子节点
		for(int i = a.length / 2; i >= 0; i--)heapOne(a, a.length, i);
		
		//边输出堆顶元素,边调整
		int n = a.length;//剩余元素
		while(n > 0){
			System.out.print(a[0] + "  ");//输出堆顶元素
			a[0] = a[n - 1]; //最后一个元素置顶
			n--;
			heapOne(a, n, 0);//筛选第一个元素
			
		}
		System.out.println();
	}

	

}

输出结果为:

1  3  8  11  22  55  

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值