什么是堆?
堆是一种特殊的完全二叉树,
即自上而下, 自左到右, 依次将每一个节点, 叠满整棵树, 这种树叫完全二叉树
当完全二叉树的每一个节点大于等于或者小于等于自己的左右子树时, 称该完全二叉树为堆
当堆的顶端是最大值时, 它叫大顶堆
当堆的顶端是最小值时, 它叫小顶堆
堆可以用来解决什么样的问题?
TOP K问题(面试老常客了, 希望你不是和我一样面向面试学习。。。
堆排序的理论思想
虽然堆排序引入了堆的概念, 但是并不是真正的去构建一个二叉树, 它只是实现了一个"假想的逻辑堆 a[i] >= a[2 * i + 1] && a[i] >= a[2 * i + 2]
先来个无序的堆!
我们把它从上往下, 从左往右给它标上号(根为i),
like this!
并且可以把它换成一个无序数组
我们准备把它构造成一个合格的大顶堆
先构建一个大顶堆, 构建一个大顶堆的步骤, 需要从下至上的构建, 所以我们需要从第一个非叶子结点出发。(在这个图中就是1号节点(值为9的哪个
第一个非叶子节点处理完了, 现在我们来处理第二个非叶子节点
构建好大顶堆之后, 我们再将堆顶的元素也就是最大元素放入堆的最后一个位置, 从而生成了第一个有序的元素, 如此再重新调整混乱的大顶堆, 再依次地调整好的大顶堆–堆顶元素放入尾部
是不是感觉就得到了第一个最大值(TOP K的内味儿有了!
更换完堆顶元素后, 再对堆依次进行调整
这样不停的替换调整, 最后将得到一个有序的数组
我们来总结一下!
1).将无序序列构建成一个堆,根据升序降序需求选择大顶堆或小顶堆;
2).将堆顶元素与末尾元素交换,将最大元素"沉"到数组末端;
3).重新调整结构,使其满足堆定义,然后继续交换堆顶元素与当前末尾元素,反复执行调整+交换步骤,直到整个序列有序。
实现堆排序的代码
package baguwen;
import java.util.Arrays;
public class HeapSort {
public static void main(String[] args) {
int arr[] = {4, 6, 8, 5, 9};
//Link Start!
heapSort(arr);
System.out.println(Arrays.toString(arr));
}
public static void heapSort(int arr[]) {
int temp = 0;
//将无序序列构建成一个堆, 根据升序降序需求选择大顶堆或小顶堆
for (int i = arr.length / 2; i >= 0; i--) {
//除开叶子节点, 从下往上, 从左往右对无序数组(堆)进行排序
adjustHeap(arr, i, arr.length);
}
for (int j = arr.length - 1; j > 0; j--) {
//对排序后的把根节点和序号最大的叶子节点进行交换
//(这样就获得了一个最大的数
temp = arr[j];
arr[j] = arr[0];
arr[0] = temp;
//交换完后对剩下的堆进行整理(除去了序号最大的叶子节点)
adjustHeap(arr, 0, j);
}
}
public static void adjustHeap(int[] arr, int i, int length) {
//k = i * 2 + 1是i节点的左子节点
//k = k * 2 + 1相当于k节点的左子节点
for (int k = i * 2 + 1; k < length; k =k * 2 + 1) {
//比较两个叶子节点他们值的大小(如果存在), k指向那个较大的那个
if (k + 1 < length && arr[k] < arr[k + 1]) {
k++;
}
//如果叶子节点大于它的父节点, 那么进行交换
if (arr[k] > arr[i]) {
int temp = arr[i];
arr[i] = arr[k];
arr[k] = temp;
i = k;
//如果不大于则进行下一次循环(就不需要进行交换了
}else {
break;
}
}
}
}