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)。