完全二叉树:在一个满二叉树中,在最下层从最右侧起,去掉相邻的若干叶子结点,得到的二叉树称为完全二叉树。
堆其实就是一棵完全二叉树。
大顶堆
堆顶元素(即第一个元素)为最大项,并且二叉树的父结点都大于子结点。
小顶堆
堆顶元素为最小项,并且二叉树的父结点都小于子结点。
堆排序
堆排序就是利用堆进行排序的算法,它的基本思想是,将待排序的序列构成一个大顶堆。此时整个序列的最大值就是堆顶的根结点。将它移走,然后将剩余的n-1个序列重新构造成一个堆,这样就可以得到n个元素中的次大值。如此反复循环就能得到有序序列。
例子
假设我们要排序的序列是{50,10,90,30,70,40,80,60,20},一共9个数,我们第一步就是构建一个堆,也就是完全二叉树:
第二步,将该堆变为一个大顶堆,也就是说,如果子结点的值大于父结点,那么就将该子结点与父结点交换位置:
第三步:将最上面的堆顶元素与堆末尾的元素进行交换:
第四步,将除了最后一个元素以外的剩余8个元素再排成一个大顶堆:
再将最上面的堆顶元素与堆末尾的元素进行交换,然后再把剩下的7个元素重新排成大顶堆...重复下去,最终就可以排序成功。
代码实现
import java.util.Arrays;
public class HeapSort {
public static void main(String[] args) {
int[] arr = {50,10,90,30,70,40,80,60,20};
heapsort(arr);
System.out.println(Arrays.toString(arr));
}
public static void heapsort(int[] arr) {
int len = arr.length;
for(int i=len/2-1; i>=0; i--) { //把arr[]构造成大顶堆
heapAdjust(arr, i, len);
}
//对调,然后再次成为大顶堆
for(int i=len-1;i>=0;i--) {
swap(arr, 0, i);
heapAdjust(arr, 0, i);
}
}
/**
*
* @param arr 要处理的数组
* @param i 父结点下标
* @param size 要处理数组部分的长度
*/
public static void heapAdjust(int[] arr,int i,int size) {
int largest = i; //largest为最大值的下标
int left = i * 2 + 1; //左孩子
int right = i* 2 + 2; // 右孩子
if(left < size && arr[left] > arr[largest]) {
largest = left;
}
if(right < size && arr[right] > arr[largest]) {
largest = right;
}
if(largest != i) { 父结点不是最大值
swap(arr, largest ,i);
//对子树进行调整
heapAdjust(arr, largest, size);
}
}
public static void swap(int[] arr, int a, int b)
{
int temp = arr[a];
arr[a] = arr[b];
arr[b] = temp;
}
}
特点
- 由于它在直接选择排序的基础上利用了比较结果形成。效率提高很大。它完成排序的总比较次数为O(nlog2n)。
- 堆排序需要两个步骤,一个建堆,而是交换重新建堆。比较复杂,所以一般在小规模的序列中不合适,但对于较大的序列,将表现出优越的性能