堆排序是选择类排序算法,核心思想依然是选择最大(小)的元素,与直接选择排序不同的是,堆排序借助了最大堆的特性,使得最大元素的查找更加的便利。
最大堆
最大堆是二叉堆的一个形式,最大堆要求根节点的关键字是所有节点关键字中的最大者,即要求最大堆根节点的关键字不小于其左叶子的关键字,也不能小于右叶子的关键字。这就要求最大堆:
- 最大堆的子树也是一个最大堆;
- 最大堆是完全二叉树,而且是根节点关键字大于等于左右叶子关键字的完全二叉树
既然最大堆是完全二叉树,则根节点与其叶子节点节点之间的存在以下关系:
- 子节点A[i]的父节点 A[Parent(i)]=A[(i-1)/2]
- 父节点i的左叶子节点 A[left(i)]=A[2*i+1]
- 父节点i的右叶子节点 A[left(i)]=A[2*i+2]
算法描述
- 通过待排序的无序队列自下而上构建最大堆;
- 交换最大堆首尾两个元素,则最大元素被交换到队尾,并移除已排序的最大元素
- 由于队列顶端新元素的加入破坏了原有的最大堆状态,重新调整为最大堆;
- 重复2)3)步骤,知道排序完成。
算法实现
1.调整当前堆为最大堆
/**
* 将堆调整当前的堆为最大堆
*
*/
private static void ajustHeap(int[] arr, int parent, int heap_size) {
// 比较左叶子和根节点
int left = parent * 2 + 1;
if (left >= heap_size)
return;
int max_index = parent;
if (arr[left] > arr[parent]) {
max_index = left;
}
int right = parent * 2 + 2;
if (right < heap_size && arr[max_index] < arr[right]) {
max_index = right;
}
// 保证当前节点为最大堆
if (max_index != parent) {
swap(arr, parent, max_index);
// 新交换的元素所在的枝干要调整为最大堆
ajustHeap(arr, max_index, heap_size);
}
}
2.构建最大堆
/**
* 构建最大堆
*/
private static void buildMaxHeap(int[] arr) {
// 最后一个叶子节点的父节点
int last_branch = (arr.length - 1) / 2;
for (int i = last_branch; i >= 0; i--) {
ajustHeap(arr, i, arr.length);
}
}
3.堆排序
public static void sort(int[] arr) {
if (arr == null || arr.length < 2)
return;
// 构建最大堆
buildMaxHeap(arr);
int heap_size = arr.length;
// 最大堆的顶元素为最大元素
for (int i = arr.length - 1; i > 0; i--) {
swap(arr, 0, i);
ajustHeap(arr, 0, --heap_size);
}
}