大根堆和小根堆
堆是一种完全二叉树,每个结点的值都大于或等于其左右孩子结点的值,称为大根堆;或者每个结点的值都小于或等于其左右孩子结点的值,称为小根堆。
堆排序
堆排序是一种选择排序,利用大根堆或小根堆的特性,堆顶的值总是最大或是最小的。以大根堆为例,将n个待排序的元素建立大根堆,弹出堆顶的最大值与堆末尾元素交换,此时末尾就是该序列最大值。再将剩下n-1个元素建立大根堆,继续弹出堆顶最大值与堆末尾元素交换,如此反复执行,最终会得到升序的序列。
底层用数组存储堆的话,数组[0]不使用,以数组[1]为数组的第一个元素,若某个元素下标为i,则它的左子树下标为( i * 2 ),右子树( i * 2 + 1 )。【如果使用数组[0],那么左子树下标为( i * 2 + 1 ),右子树的下标为( i * 2 + 2 )】
堆排序的Java实现
堆排序
public static void heapSort(int[] array){
//建大根堆
buildHeap(array);
for (int i = array.length - 1; i > 1 ; i--) {
//将堆顶最大值放到堆末尾
int temp = array[i];
array[i] = array[1];
array[1] = temp;
//调整堆
adjustHeap(array,i - 1 , 1);
System.out.print("第" + (array.length - i)+ "次:");
display(array);
}
}
建大根堆
public static void buildHeap(int[] array){
for(int i = (array.length - 1)/ 2 ; i > 0 ; i--){
adjustHeap( array , array.length - 1 , i);
}
}
调整n个元素为大根堆
public static void adjustHeap(int[] heap ,int heapSize , int i){
int left = i * 2 ;
int right = i * 2 + 1;
//左右子树的较大大值
int max = i;
//取左右子树最大值
if( left <= heapSize && heap[max] < heap[left] ){
max = left;
}
if( right <= heapSize && heap[max] < heap[right] ){
max = right;
}
if ( max != i){
//交换max和i的数值,并在max位置递归调用adjustHeap,保证交换后已经符合堆的规则
int temp = heap[i];
heap[i] = heap[max];
heap[max] = temp;
adjustHeap(heap , heapSize , max);
}
}
数组打印方法
public static void display(int[] array){
for (int i = 1 ; i < array.length ; i++){
System.out.print(array[i] + " ");
}
System.out.println();
}
测试代码与运行结果
测试
public static void main(String[] args) {
int[] a = new int[]{0,4,6,1,2,9,7,2,3,8};
heapSort(a);
System.out.print("排序后:");
display(a);
}
运行结果
第1次:8 5 7 4 1 6 3 2 9
第2次:7 5 6 4 1 2 3 8 9
第3次:6 5 3 4 1 2 7 8 9
第4次:5 4 3 2 1 6 7 8 9
第5次:4 2 3 1 5 6 7 8 9
第6次:3 2 1 4 5 6 7 8 9
第7次:2 1 3 4 5 6 7 8 9
第8次:1 2 3 4 5 6 7 8 9
排序后:1 2 3 4 5 6 7 8 9
算法特点
- 堆排序算法是不稳定的排序算法
如某序列为(7,4,5,5,3),在进行堆调整时,第二个5将与4交换位置,导致第二个5和第一个5交换了位置。 - 时间复杂度O(n*logn)
- 空间复杂度O(1)