堆排序
堆排序是另一种选择排序方法,它是树型选择排序的改进,它使用的辅助空间较少,仅需要一个元素用于空间交换。
堆:根结点的关键码值或者大于左右子树或者都小于左右子树,而且左右子树也是堆。
如果每个结点的值都大于左右子树关键码值,称之为大顶堆。每个结点的值都小于左右子树的关键码值,称之为小顶堆。
首先,堆是一个完全二叉树。堆排序包含两个过程:
(1)初建堆
(2)调整堆
调整堆:对于第二个过程,描述如下,第一次,取出堆顶元素与R[n]进行交换,然后从根结点开始调整堆,根结点与左右孩子中较大者交换,这样,左右子树的堆会被破坏,继续进行上述操作,直到叶子结点为止。第二次,取出堆顶元素与R[n-1]交换,然后调整堆。
初建堆:从非终端结点开始一直到根结点,执行调整堆的过程。这样,就会建立一个大顶堆。
如果排序过程中,是按照从小到大正序,那么建立一个大顶堆。如果是逆序就建立一个小顶堆。
堆排序算法的JAVA实现:
/**
* 堆排序包括两个步骤 (1)初始堆(堆的定义:(1)堆是一个完全二叉树(2)根结点的值或者大于左右子树的值或者小于左右子树的值(3)左右子树也是一个堆)
* (2)调整堆(当初始小顶堆之后,堆顶元素是最小的元素,取出最小的元素与最后一个元素相交换,再把剩下n-1个元素调整成堆,依次调整直到1为止)
* 非终端节点调整 初始堆时从n/2向上调整成堆 把第一个元素与最后一个元素交换之后从第1个元素开始调整成新堆
*
* @author Administrator
*
*/
public class HeapSort {
public static void main(String[] args) {
int num[] = new int[] { 0, 5, 40, 32, 2, 2221 };
heapsort(num, 5);
for (int x = 0; x < num.length - 1; x++) {
System.out.print(num[x + 1] + " ");
}
}
//调整堆
public static void adjustHeap(int[] num, int s, int t) {
int i = s;
int x = num[s];
for (int j = 2 * i; j <= t; j = 2 * j) {
if (j < t && num[j] < num[j + 1])
j = j + 1;// 找出较大者把较大者给num[i]
if (x > num[j])
break;
num[i] = num[j];
i = j;
}
num[i] = x;
}
public static void heapsort(int[] num, int n) {
// 初始建堆从n/2开始向根调整
int i;
for (i = n / 2; i >= 1; i--) {
adjustHeap(num, i, n);//初始堆过程
}
for (i = n; i > 1; i--) {
num[0] = num[i];// 将堆顶元素与第n,n-1,.....2个元素相交换
num[i] = num[1];
num[1] = num[0];// 从num[1]到num[i-1]调整成新堆
adjustHeap(num, 1, i - 1);
}
}
}
性能分析:
时间复杂度:
堆是一个完全二叉树,设树高为k=log2n+1,从根到叶的调整,关键码比较的次数为2(k-1),交换的次数至多为k次。所以比较的次数不超过
2*(log2(n-1)+log2(n-2)+....+log22)<2nlog2n
而比较的次数不超过4n.所以堆排序的时间复杂度为O(nlogn).
空间复杂度:
需要一个单元的辅助空间用于交换,所以辅助空间为O(1).
稳定性:
堆排序调整过程中存在着交换,所以堆排序是一个不稳定的排序算法。
堆排序的实例过程可以参考http://blog.sina.com.cn/s/blog_534408920100acqv.html.