前言
堆结构算是一种比较有难度的排序算法,如果大家看了这篇文章理解不了的话建议看一下视频,有些东西可能我描述的不是那么清楚。
1. 定义
堆排序(Heapsort )是利用堆这种数据结构所设计的一种排序算法。
2.堆的定义
堆是具有以下性质的完全二叉树∶
ⅰ. 每个结点的值都大于或等于其左右孩子结点的值,称为大顶堆;
ⅱ. 每个结点的值都小于或等于其左右孩子结点的值,称为小顶堆。
以大顶堆为例,下图是大顶堆的图
我们对堆中的结点按层进行编号,将这种逻辑结构映射到数组中就是下面这个样子:
0 | 95 |
---|---|
1 | 94 |
2 | 66 |
3 | 75 |
4 | 63 |
5 | 33 |
6 | 22 |
7 | 55 |
8 | 44 |
9 | 11 |
从上面的图中,我们可以总结出规律
对于某1个节点i,它的父节点(i-1)/2
对于某1个节点i,它的左孩子2i+1
对于某1个节点i,它的右孩子2i+2
最后一个非叶子节点为(arr.length/2-1)(忽略小数点)
3.堆排序的思路
以大顶堆为例
(1)根据初始数组去构造初始堆(可以看成一个完全二叉树)。
(2)每次交换第一个和最后一个元素,输出最后一个元素(最大值),然后把剩下元素重新调整为大根堆,再进行交换第一个元素和最后一个元素,再调整大顶堆,重复执行,直到整个数组排序完成。
4.代码实现(大顶堆为例)
import java.util.Arrays;
public class Heapsort {
public static void main(String[] args) {
int[] arr = {3,2,1,4,6,77,22,11,22};
heapsort(arr);
System.out.println(Arrays.toString(arr));
}
public static void heapsort(int[] arr){
//建初始堆
for (int i = arr.length/2-1;i >= 0; i--){
heapify(arr, i, arr.length-1);
}
//堆排序的过程
for (int i = arr.length - 1; i > 0; i--){
swap(arr,0,i);
heapify(arr,0,i-1);
}
}
//把一个非堆结构变成堆结构
public static void heapify(int[] arr, int i, int last_index){
int max = i;
if (2*i+1 <= last_index && arr[max] < arr[2*i+1]){
//记录最大值节点
max = 2*i+1;
}
if (2*i+2 <= last_index && arr[max] < arr[2*i+2]){
//记录最大值节点
max = 2*i+2;
}
if (max != i){
swap(arr,i,max);
heapify(arr, max, last_index);
}
}
//交换
public static void swap(int[] arr, int i, int j){
int temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
}
5.算法特点
算法稳定性:不稳定,因为在堆顶和堆底交换的时候两个相等的记录在序列的相对位置可能发生变化。
时间复杂度:O(n+nlog2^n) - O(nlog2^n)