目录
1.数组排序
- 我们可以运用堆这个算法对数组进行排序的实现,比如创建一个新的堆,利用堆的add()遍历数组将元素加入堆中,但是这样会增加O(n)的辅助空间,那么有没有一种办法,利用堆使用O(1)的空间就实现数组排序呢?
- 答案当然是有的,就是我们接下来讲的原地堆排序
2.原地堆排序
- 首先我们将数组进行堆化,(数组单调不减用大堆,单调不增用小堆)
- 将堆顶元素与堆末尾的元素进行交换,可取size--,这样最末尾的元素就已经排好序了
- 将堆顶元素下沉,继续和最后一位没排好序的元素进行交换,重复23操作
- 直到换到堆顶最后一位元素,数组内所有元素都已排好序,退出循环
3.图解(以数组单调不减用大堆为例)
步骤2中,蓝色代表已排好序的元素,绿色代表正在下沉的元素
4.代码实现:
public static void heapSort(int [] arr){
//堆化
//从倒数第一个不是叶子节点开始遍历下沉
int k = (arr.length - 1 - 1)/2;
for (int i = k; i >= 0 ; i--) {
siftDown(arr,i,arr.length);
}
//此时小堆已建立完成
for (int i = arr.length - 1; i > 0 ; i--) {
//交换首尾元素
swap(arr,0,i);
//首元素下沉
siftDown(arr,0,i);
}
}
public static void siftDown(int[] arr, int k, int size){
//保证有左子树
while ((k * 2) + 1 < size){
int j = (k*2) + 1;
//有右子树并且右子树比左子树小
if(j + 1 < size && arr[j + 1] < arr[j]){
j = j + 1;
}
//此时找到左右子树最小值
if(arr[k] > arr[j]){
swap(arr,j,k);
k = j;
}else {
break;
}
}
}
5.测试
数据量小的情况下测试数据可能不准,所以我们用随机数随机产生一万个随机数,然后调用堆原地排序方法,然后用isSorted()测试结果是否有序
public static boolean isSorted(int[] arr){
for (int i = 0; i < arr.length - 1; i++) {
if(arr[i] < arr[i + 1]){
return false;
}
}
return true;
}
public static void main(String[] args) {
Random random = new Random();
int n = 1_0000;
int[] data= new int[n];
for (int i = 0; i < n; i++) {
data[i] = random.nextInt(100);
}
System.out.println("调用堆排序之前,数组元素是否有序?");
System.out.println(isSorted(data));
heapSort(data);
System.out.println("调用堆排序之后,数组元素是否有序?");
System.out.println(isSorted(data));
}
测试结果:
我们可以看到 ,经过原地堆排序后的数组变得有序了,证明我们的代码是没问题哒