1.堆排序(以大顶堆为例)
(1).堆排序简介:
堆排序是一个时间复杂度为O(nlog2n)(初始化堆的次数n(几个排序的数字初始化堆几次) × 建堆的过程 log2n(找最大数字的过程))、空间复杂度为O(1)(排序过程并不需要新的空间来存储数据)以及非稳定(建堆的过程可能会改变两个相同数字的相对位置)的排序算法。
(2).堆排序对数据结构的要求:
堆排序在排序过程中必须维护一个完全二叉树.我们使用数组来模拟堆,并以下标的方式来构建父节点与子节点存在的物理关系。
(3).堆排序详解
①建立初始堆:从最后一个非叶子节点的父节点开始(计算方式:(数组的长度 - 1 / 2 )- 1),当前父节点与两个子节点进行大小比较,最大的节点应被交换到父节点位置,这一比较过程应从最后一个父节点到根节点,所以是自下而上的。
②构造顺序序列:把当前根节点的值和最后一个叶子节点进行交换,这时数组长度减一(叶子节点减一),因为已交换的数字已经排序过。交换过后的堆已不满足大顶堆,因为根节点的值已不是最大值,所以这时的比较操作是从根节点到最后一个父节点,这是一个自上而下的过程。这样的构造排序的过程应维持到仅仅只有最后一个根节点。
(4).堆排序代码(Java):
/**
* @author Cookie
* @Project: DataStructuresAndAlgorithms
* @Package:sort
* @date 2019/6/23 12:34
* @description 堆排序
**/
public class HeapSort {
public int[] sort(int[] array){
if (array == null || array.length < 1){
return null;
}
//第一步应建立初始堆,这是唯一一个自下而上的过程
buildHeap(array);
int length = array.length;
//第二步是构造顺序序列的过程,并且应该不停的构建大顶堆,直到仅剩一个根节点
while (length > 1){
//构建顺序序列
swap(array,0,length - 1);
//数组长度减一(当前叶子是已排序的节点,所以应该减去)
length--;
//构建大顶堆
heapfy(array,0,length);
}
return array;
}
//构建堆
private void heapfy(int[] array, int start, int length) {
//对于最开始的建堆来说,只是比较当前父节点与其左右子节点的值
//对于后续交换根节点之后从根节点开始自上而下的比较时,就需要将根节点值一直进行比较直到它到合适的位置
while (true){
//当前父节点的左子节点,如果根节点是从下标1开始的,那么左子节点为 2 * start,如果根节点是从下标0开始的,那么左子节点为 2 * start + 1.
int left = 2 * start + 1;
//当前父节点的右子节点,如果根节点是从下标1开始的,那么右子节点为 2 * start + 1,如果根节点是从下标0开始的,那么右子节点为 2 * start + 2.
int right = 2 * start + 2;
//当前被交换后节点的最大值
int maxPosition = start;
if (left < length && array[left] > array[maxPosition]){
maxPosition = left;
}
if (right < length && array[right] > array[maxPosition]){
maxPosition = right;
}
//说明父节点本身是或经过交换后已经是最大值,所以不需要再次进行交换
if (maxPosition == start){
break;
}else {
int temp = array[maxPosition];
array[maxPosition] = array[start];
array[start] = temp;
//这一步对于自上而下的构建来说相当重要,这会将比较操作继续进行下去直到越界或者已满足堆的条件
start = maxPosition;
}
}
}
//交换
private void swap(int[] array, int i, int j) {
int temp = array[i];
array[i] = array[j];
array[j] = temp;
}
//自低向上建堆
private void buildHeap(int[] array) {
//计算最后一个含有叶子节点的父节点: array.length - 1 / 2 - 1
for (int i = ( array.length - 1 / 2 )- 1; i >= 0; i--){
heapfy(array,i,array.length);
}
}
}
(5).堆排序的优点及缺点:
优点:无论怎么的序列在通过堆排序的方式进行排序之后,它的时间复杂度都是O(n*log2n),并且不需要耗费额外的空间,这可以说是排序算法中时间和空间都最优秀的算法了。
缺点:维护堆的过程相对麻烦,因为在面对增删改数据的过程都需要去维护。