堆排序详解(JAVA版)


package com.hsit.heap;

import java.util.Scanner;

/**
 * 
 * @author Colo
 * @version 创建时间:2011-11-1 上午11:44:10
 */
public class Heap {

	private int[] data;

	/*输入数组元素,数组下标从1开始*/
	public void input() {
		System.out.println("请输入数组大小");
		Scanner scanner = new Scanner(System.in);
		int n = scanner.nextInt();
		data = new int[n + 1];
		System.out.println("输入数组元素");
		for (int i = 1; i <= data.length-1; i++) {
			data[i] = scanner.nextInt();
		}
		System.out.println("输入完成");
	}
	
	/**
	 * 调整堆,使其满足堆得定义
	 * @param i
	 * @param n
	 */
	public void adjust(int i, int n) {
		int child;
		for (; i <= n / 2; ) {
			child = i * 2;
			if(child+1<=n&&data[child]<data[child+1])
				child+=1;/*使child指向值较大的孩子*/
			if(data[i]<data[child]){
				swap(i, child);
				/*交换后,以child为根的子树不一定满足堆定义,所以从child处开始调整*/
				i = child;
			}
		}
	}

	public void heapSort() {
		/*根据树的性质,树节点前一半一定是分支节点,即有孩子的,所以我们从分支节点开始调整出初始堆*/
		for (int i = data.length / 2; i > 0; i--)
			adjust(i, data.length-1);
		for (int i = data.length-1; i > 0; i--) {
			/*把根节点跟最后一个元素交换位置,调整剩下的n-1个节点,即可排好序*/
			swap(1, i);
			adjust(1, i - 1);
		}
	}

	public void swap(int i, int j) {
		int temp = data[i];
		data[i] = data[j];
		data[j] = temp;
	}

	public void print() {
		for (int i = 1; i < data.length; i++) {
			System.out.print(data[i]+" ");
		}
		System.out.println();
	}

}

建堆


  如上,首先第一步,在对应的数组元素A[i], 左孩子A[LEFT(i)], 和右孩子A[RIGHT(i)]中找到最大的那一个,将其下标存储在child中。如果A[i]已经就是最大的元素,则程序直接结束。否则,i的某个子结点为最大的元素,将其,即A[child]与A[i]交换,从而使i及其子女都能满足最大堆性质。下标child所指的元素变成了A[i]的值,会违反最大堆性质,所以对child所指元素为根的子树重新调整。如下,是此adjust的演示过程下图是把4调整到最底层,一趟操作,但摸索的时间为LogN):

 

     由上图,我们很容易看出,初始构造出一最大堆之后,在元素A[i],即16,大于它的俩个子结点4、10,满足最大堆性质。所以,i下调指向着4,小于,左子14,所以,调用adjust,4与其子,14交换位置。但4处在了14原来的位置之后,4小于其右子8,又违反了最大堆的性质,所以再递归调用adjust,将4与8,交换位置。于是,满足了最大堆性质,程序结束。


排序

以下是,堆排序算法的演示过程(通过,顶端最大的元素与最后一个元素不断的交换,交换后又不断的调用adjust重新维持最大堆的性质,最后,一个一个的,从大到小的,把堆中的所有元素都清理掉,也就形成了一个有序的序列。这就是堆排序的全部过程。):

 

 

上图中,a->b,b->c,....之间,都有一个顶端最大元素与最小元素交换后,调用adjust的过程,而要完成整个堆排序的过程,共要经过O(n)次这样的adjust操作。所以,才有堆排序算法的运行时间为O(n*lgn)。



评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值