堆的定义
堆是一种叫完全二叉树的数据结构,可以分为大根堆,小根堆,而堆排序就是基于这种结构而产生的一种程序算法。
堆的种类
大根堆:每个节点的值都大于其子节点的值(权重)
小跟堆:每个节点的值都小于其子节点的值
说明 :
从数据存储来看,数组存储方式和树的存储方式可以相互转换,即数组可以转换成树,树也可以转换成数组。
以大根堆为例子
堆排序思路:
如何利用堆这种数据结构进行排序?
堆排序是建立在大根堆(小根堆)上的,我们知道在根节点就是此数组最大的数,此时可以确定一个位置,当我们把第一个位置和最后一个位置进行交换后,如下图所示,发现,除去最后一个元素,从根节点开始,可以很快的重新构建一个大根堆
重新构建大根堆如图所示
此时新大根堆的的最大值也已经确定,为根节点的值(35),此时我们重复上一步骤,把最大的数与数组(此时的数组是指除去45的数组)最后的数交换,除去最后一个数,组成新的数组,从根节点开始,又可以很快的重新构建一个大根堆..
重复这个步骤,最终可得到有序的数组
最后思路我们有了,但是如何把无序的数组构建成大根堆呢?
大根堆的创建应该从下往上,不能直接从根节点开始,否则有的不符合大顶堆的定义
- 从最后一棵子树开始,从后往前调整(从数组角度来看)
- 每次调整从上往下调整(从堆角度来看)
- 调整为大根堆
文字表达不力,请各位直接看图,直观了解
图片来自博客http://t.csdn.cn/7iRl6
总结步骤
1、初始化数组,创建大顶堆。
1.1大顶堆的创建从下往上比较,不能直接用无序数组从根节点比较,否则有的不符合大顶堆的定义。
2、交换根节点和倒数第一个数据,现在倒数第一个数据就是最大的。
3、重新建立大顶堆。
3.1 因为只有 array[0] 改变,其它都符合大顶堆的定义,所以可以根节点 array[0] 重新建立。
4、重复2、3的步骤,直到只剩根节点 array[0],即 i=1。
下面给出Java示例代码
public class HeapSort {
public static void main(String[] args) {
int arr[] = {2, 5, -1, 35, 23, 3, 6, 45, 8, 9};
heapSort(arr);
}
//编写一个堆排序的方法
public static void heapSort(int arr[]) {
int temp = 0;
System.out.println("堆排序");
//首次构建大顶堆
for (int i = arr.length / 2 - 1; 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);
}
System.out.println("调整: " + Arrays.toString(arr));
}
//将一个数组(二叉树),调整成一个大顶堆
/**
* @param arr 待调增的数组
* @param i 表示非叶子结点在数组中索引
* @param length 表示要构建的大顶堆(数组)的长度
*/
public static void adjustHeap(int[] arr, int i, int length) {
int temp = arr[i];
for (int k = i * 2 + 1; k < length; k = k * 2 + 1) {
if (k + 1 < length && arr[k] < arr[k + 1]) {
k++;
}
if (arr[k] > temp) {
arr[i] = arr[k];
i = k;
} else {
break;
}
}
arr[i] = temp;
}
}