排序系列【比较排序系列之】堆排序
堆排序,顾名思义是通过直接选择排序衍生而来的。直接选择排序是直接从剩余记录中线性的查找最大记录的方法,并没有巧妙的利用前一轮查找所得到的信息,而堆排序,利用堆数据结构来保存剩余记录相对大小的信息,因而是更有效的选择排序。
堆分为最大堆和最小堆,本篇我们通过最大堆来实现我们的功能。
最大堆需要满足的条件: 堆中每个父节点中的数据项都要大于或等于其子节点中的数据项。
堆排序主要有两个步骤:
- 对所有记录建立一个最大堆。
- 取出堆顶的最大记录与数组末端进行交换,最大记录放在下标n-1的位置;
- 对剩余堆记录进行调整,再次形成一个最大堆;
- 再次取出对顶的最大记录与数组末端进行交换,最大记录放在下标n-2的位置;
- 不断重复,直到堆为空,也就是排序完成。
示例数组如下:【49,38,65,97,76,13,27,49】
通过筛选法建最大堆的前提条件:
- 堆的初始位置从0开始,依次递增;
- 若父结点的位置为i;则左孩子结点位置为2i+1;右孩子结点位置为2i+2;
筛选位置从最后一个非结点编号开始,也就是n/2-1向下取整。
初始堆如下:
筛选位置从最后一个非结点编号开始,n=8,所以初始筛选位置为i=3,也就是i=97;
因为97>49,所以位置不变;然后继续比较i,i–;
i=2时,因为13<27,且65>27,所以位置依旧不变。
i=1时,因为97>76,所以比较38和97,因为38<97所以交换位置;又因为38<49所以继续交换位置,最后堆位置如下:
i=0时,因为97>65,所以比较49和97,因为49<97,交换位置;又因为49<76,继续交换位置,最后堆位置如下:
到此位置,排序完成,堆变成了一个最大堆。
接下来则进行交换流程,将n-1位置的值与堆顶位置的值进行交换;
1、 i=7;交换位置7上的值和堆顶的值
交换完毕,再次调整除了i=7之外的堆元素,再次转换成一个最大堆。
2、当i=6;交换位置6和堆顶的值,然后调整属于的元素;
3、当i=5;交换位置5和堆顶的值,然后调整属于的元素;
…
当i=1时;交换位置1和堆顶的值,交换流程到此结束,最后的堆如下:
以上是针对堆排序的分析流程,如下则是代码:
public static void main(String[] args) {
int[] array = {49,38,65,97,76,13,27,49};
heapSort(array,array.length);
}
/**
* 堆排序的主过程
* @param array
* @param n
*/
static void heapSort(int[] array, int n) {
int i;
int temp;
for (i = n / 2 - 1; i >= 0; --i) {
sift(array, i, n - 1);
}
for (i = n - 1; i > 0; --i) {
temp = array[0];
array[0] = array[i];
array[i] = temp;
sift(array, 0, i - 1);
}
}
/**
* 调整函数
* @param arr
* @param low
* @param high
*/
static void sift(int arr[], int low, int high) {
int i = low;
int j = 2 *i + 1;
int temp = arr[i];
while (j <= high) {
//判定左孩子结点和右孩子结点的大小,进而决定跟结点到底与谁作比较
if (j < high && arr[j] < arr[j + 1]) {
++j;
}
if (temp < arr[j]) {
arr[i] = arr[j];
i = j;
j = 2 * i + 1;
} else {
break;
}
}
arr[i] = temp;
}
而时间复杂度,我们通过以上分析和代码都可以发现,其堆排序的过程其实就是建最大堆和交换最大值之后重建堆的过程。
对于有N个结点的堆 ,其层数为logN.设置i表示二叉树的层编号,则第i层的结点数最多有2^i个。
就拿n=8举例:i=0,1,2,3
当i=3时,该层含有的最多结点个数为2^3=8个,因为是最底层,所以不需要比较,比较个数为logN-i=0;
当i=2时,该层含有的最多结点个数为2^2=4个,需要和其孩子结点(比较到最后一层)进行比较,logN-2=1;
当i=1时,该层含有的最多结点个数为2^1=2个,需要和其孩子结点进行比较(比较到最后一层)进行比较,logN-1=2次,所以当n=8时,logN=3,需要的比较次数为:2^3(3-3)+2^2(3-2)+2^1(3-1);
所以当结点数为N时,堆排序需要的比较次数为:∑求和,logN,i=0开始,2^i(logN-i).
而对于重建堆的过程,待定。