堆的基本概念:
堆是一棵所有元素按完全二叉树的顺序存储方式存储的完全二叉树。
堆的性质:
--堆中某个节点的值总不大于或不小于其父节点的值;
--堆总是一棵完全二叉树。
小根堆:每个节点的值小于等于左、右孩子的值(根节点最小的堆)
大根堆:每个节点的值大于等于左、右孩子的值(根节点最大的堆)
父节点 n ==> 左孩子:2*n+1、右孩子:2*n+2
孩子节点 n ==> 父节点:(n - 1)/2
时间复杂度:On * log 2n)
空间复杂度:O(1)
稳定性:不稳定
堆排序的具体实现步骤:
1. 将无序的序列构建成堆,根据要拍的升序降序选择建立大根堆还是小根堆(升序建立大根堆,降序建立小根堆);
1.1 按照完全二叉树的顺序存储方式,先将待排数组转换为完全二叉树
1.2 建立大根堆
从当前完全二叉树的最后一个子树开始调整,找孩子节点中的最大值与父节点进行交换,交换完成后往前走比较倒数第二棵子树,依次往前,直到每个子树的父节点大于等于孩子节点。
如下图先从父节点为67的子树开始,左孩子78大于父节点,将二者进行交换:
比较父节点为56的子树,右孩子89较大,将二者进行交换,得到以89为父节点的整个子树是大根堆:
比较以34为父节点的子树,其中左孩子91最大,将其与父节点34交换:
最后调整以45为父结点的子树,右孩子91最大,将其与45进行交换后可得到大根堆(如果交换后发现以45为父节点的子树孩子节点大于父节点,须对其进行调整,保证每个节点大于等于左右孩子节点):
2. 交换堆顶元素与末尾元素,将最大值换到数组末端;
3. 重新调整,使其仍为大根堆(小根堆),此时不包括末尾元素;
4. 循环2、3步骤,直到整个序列都有序。
代码实现:
import java.util.Arrays;
public class HeapSort {
public static void main(String[] args) {
int[] array = {45,56,34,67,89,91,12,78,23};
System.out.println("排序之前:"+Arrays.toString(array));
HeapSort(array);
System.out.println("排序之后:"+Arrays.toString(array));
}
public static void HeapSort(int[] array){
//向下(从下往上)调整建立大根堆
//i为子节点,(array.length-2)/2为父节点
for (int i = (array.length-2)/2; i >= 0 ; i--) {
AdjustDown(array,i,array.length);
}
//交换堆顶和最后一个下标
int end = array.length - 1;
while (end > 0){
int tmp = array[0];
array[0] = array[end];
array[end] = tmp;
AdjustDown(array,0,end);
end--;
}
}
//向下调整,从每个子树的根节点开始调整,调整的长度为length
private static void AdjustDown(int[] array, int root, int length) {
int parent = root;
int left = 2*parent+1;//左孩子节点
int right = 2*parent+2;//右孩子节点
int child;//孩子节点中的最大值
while (left < length){
if ( right < length && array[left] < array[right]){
child = right;
}else {
child = left;
}
//孩子节点大于父节点,进行交换,然后
if (array[child] > array[parent]){
int tmp = array[child];
array[child] = array[parent];
array[parent] = tmp;
parent = child;
left = 2*parent +1;
right = 2*parent+2;
}else {
break;
}
}
}
}
运行结果: