常见排序--堆排序
因为堆结构是一个典型的完全二叉树,所以堆可以用一个一维数组表示。父结点与左子结点、右子结点的关系很容易推到出。推导过程如下:
假设数组的下标是:0 , 1 , 2 , 3 , 4 , 5, 6 , 7 , 8
则结点关系如下:
parent left right
0 1 2
1 3 4
2 5 6
3 7 8
我们很容易找出它们之间的关系:(1-1)/2 = 0 , (2-1)/2 = 0 ;
(3-1)/2 = 1, (4-1)/2 = 1;
(5-1)/2 = 2, (6-1)/2 = 2;
.....
所以任意结点n的父结点:parent = (n - 1) /2
而左子结点n与父结点的关系是上个函数的反函数,即:n = parent * 2 + 1;右子结点m的下标显然始终比左子结点n大1,即:m = parent * 2 + 2
根据上述公式,我们可以轻易写出,parent() , left() ,right() 函数,详见以下实现。
完成堆排序,主要有三个模块:MaxHeap, BuildHeapify 和HeapSort。
MaxHeap功能很简单,可简单认为是比较三个数的大小,只是这三个数由parent(),left(),right()进行确定。
BuildHeapify是建最大(小)堆,从非叶子节点开始,并且从后向前“最大(小)堆”。完全二叉树的非叶子结点个数计算公式:n/2 。注意,当前节点下标与n/2相差1
HeapSort根据最大(小)堆排序,我们可以类比冒泡排序。每次建堆,最大(小)值会被推到最数组的0位置。
Java实现的堆排序:
public class TestHeap {
public static void main(String[] args) {<span style="font-family: Arial, Helvetica, sans-serif;"> </span><span style="font-family: Arial, Helvetica, sans-serif;"> </span><span style="font-family: Arial, Helvetica, sans-serif;">
</span> int[] a = {3,1,3,4,51,6,7,43,132,4,45,732,79,2,2,2,1,3,5,7,8,98,9};<span style="font-family: Arial, Helvetica, sans-serif;"> </span>
Heap heap = new Heap();
heap.BuildHeapify(a, a.length);
heap.HeapSort(a);
for(int x : a){
System.out.print(x + " ");
}
System.out.println();
}
}
class Heap {
public int parent(int i){
return (i-1)/2;
}
public int left(int i){
return i*2+1;
}
public int right(int i){
return i*2+2;
}
public void MaxHeap(int[] a,int i){
int big = i;
int l = left(i);
int r = right(i);
if(a[i]<a[l]){
big = l;
}
if(a[big]<a[r]){
big = r;
}
if(big != i){
int tmp = a[i];
a[i] = a[big];
a[big] = tmp;
}
}
public void BuildHeapify(int[] a,int len){
for(int i=len/2-1;i>=0;i--){
MaxHeap(a,i);
}
}
public void HeapSort(int[] a){
for(int i=a.length-1;i>=0;i--){
BuildHeapify(a,i);
int tmp = a[0];
a[0] = a[i];
a[i] = tmp;
}
}
}