前言
本文主要介绍一种常见的排序算法——堆排序,以及堆排序的时间复杂度分析。
正文
关于堆的基本概念以及常见操作在之前的文章里已经介绍过了,这里不再赘述,有兴趣可以参考数据结构——堆以及常见操作介绍。
下面就来介绍下如何利用大根堆来进行数组排序。
堆排序思想
拿到一个无序数组后,首先做的是把无序数组转成一个大根堆,转成大根堆的过程如下:
- 把
0 ~ 1位置上的元素转成大根堆 - 把
0 ~ 2位置上的元素转成大根堆 - 把
0 ~ 3位置上的元素转成大根堆 - 把
0 ~ N位置上的元素转成大根堆
我们知道大根堆的最大元素是下标为0的那个元素,所以把一个无序数组转成大根堆后,排序思路如下:
- 将
0位置的元素和N-1位置的元素进行交换,此时N-1位置的元素为最大值 - 将
0 ~ N-2位置的元素转成大根堆,并将0位置的元素和N-2位置的元素进行交换 - 将
0 ~ N-3位置的元素转成大根堆,并将0位置的元素和N-3位置的元素进行交换 - 将
0 ~ 1位置的元素转成大根堆,并将0位置的元素和1位置的元素进行交换
经过上述步骤之后,整个无序数组就变成了有序数组。
代码实现
public void heapSort(int[] arr) {
if (arr == null || arr.length < 2) {
return;
}
// 时间复杂度O(N*logN), for循环为O(N),heapInsert为 O(logN)
for (int i = 0; i < arr.length; i++) {
heapInsert(arr, i);
}
int heapSize = arr.length;
swap(arr, 0, --heapSize);
// 时间复杂度为 O(N*logN)
while (heapSize > 0) {
heapify(arr, 0, heapSize);
swap(arr, 0, --heapSize);
}
}
public void heapInsert(int[] arr, int index) {
while (arr[index] > arr[(index - 1) / 2]) {
swap(arr, index, (index - 1) / 2);
index = (index - 1) / 2;
}
}
public void heapify(int[] arr, int index, int heapSize) {
// 左孩子的下标
int left = index * 2 + 1;
// 下方还有孩子的时候
while (left < heapSize) {
// 两个孩子中,谁的值大,把下标给largest
// 1)只有左孩子,left -> largest
// 2) 同时有左孩子和右孩子,右孩子的值<= 左孩子的值,left -> largest
// 3) 同时有左孩子和右孩子并且右孩子的值> 左孩子的值, right -> largest
int largest = left + 1 < heapSize && arr[left + 1] > arr[left] ? left + 1 : left;
// 父和较大的孩子之间,谁的值大,把下标给largest
largest = arr[largest] > arr[index] ? largest : index;
if (largest == index) {
break;
}
swap(arr, largest, index);
index = largest;
left = index * 2 + 1;
}
}
public void swap(int[] arr, int i, int j) {
int tmp = arr[i];
arr[i] = arr[j];
arr[j] = tmp;
}
时间复杂度分析
代码中,第一个for循环调用heapInsert函数,就是让数组中的元素一步步从0、0~1、0~2、0~N-1变成大根堆的过程,for循环执行了N次,时间复杂度为O(N)。在构建大根堆的时候,新加入的元素只需要和他的父节点一直到根节点比较,也就是比较了树的高度次,所以时间复杂度为O(logN)。那么在第一个for循环这个过程时间复杂度为O(N*logN)。
将无序数组转成大根堆后,进行元素的交换,一共交换了heapSize次,也就是数组的长度,所以交换的时间复杂度为O(N)。heapify元素交换的时间复杂度和元素插入类似,时间复杂度也是O(logN),所以元素整体交换的时间复杂度为O(N*logN)。
所以,两个时间复杂度相加,不过常数项是不影响整体的时间复杂度的,因此该算法的整体时间复杂度为O(N*logN)。
总结
本文主要介绍一种常见的排序算法——堆排序以及堆排序的时间复杂度分析,文中介绍了堆排序算法的思想以及时间复杂度的分析方法。