前言
前面我们学习了堆排序的两种实现方式,不知道大家还记不记得。我们之前学习的堆排序都是通过把一个无序的数组复制到堆中,然后进行排序,但是这样一来,我们就需要开辟额外的存储空间去存储。那么我们今天就来学习如何去解决这个问题,然后对堆排序进行优化。
思想:
其实我们可以的数组本身就是一个堆,只不过并不是一个最大堆,那么通过之前的学习我们可以通过shitDown操作去把数组变成一个最大堆,这个操作完全可以数组中操作,无序进行额外的空间开辟。
在进行了shitDown之后,此时我们的数组就是一个最大堆了,此时我们最大的元素就放在第一个位置。我们可以每次将最大的元素与数组最后边的数字进行交换,此时我们最大的元素就放到了正确的位置上,此时第一个位置上的元素不符合最大堆的规范,我们对这个位置进行shitDown操作,然后对【0 ,n-1】区间重复前边的操作,直至只剩下一个元素。
注意要点:
第一幅图就是对堆排序的一个直观的描述。不断的把最大值放到数组的最后边,然后数组长度进行缩减一个索引。最后数组只剩下一个元素的时候就不需要再进行操作了,此时的数组就已经是有序的了。
这里要注意,由于之前我们的堆中,我们是从索引为1的位置开始存储的,但是原地排序中,由于我们是在原来的数组中,原地排序,所以我们的数组的下标是从0开始,那么我们进行shitDown操作的时候,对于最后一个非叶子节点的判断就需要进行一下改变了。具体参照第二张图
代码实现:
package com.smarking.lzy.part2;
import com.smarking.lzy.part1.SortTestHelper;
/*
* 原地堆排序的实现
* 在目标数组中进行排序
* 思想:每次将最大的元素放到本轮区间的最后一个位置,直至只剩下一个元素
* **/
public class HeapSort3 {
/*
* 排序入口
* arr:目标数组
* size:数组大小
* */
public static void heapSort3(int [] arr) {
int size = arr.length;
if(arr == null) {
throw new RuntimeException("arr is null!");
}
//对数组进行heapify操作
for(int i = (size - 2)/2;i >= 0;i--) {
shitDown(arr,i,size);
}
for(int i = size-1;i >= 1;i--) {
SortTestHelper.swap(arr, i, 0);
shitDown(arr,0,i);//应该是在[0,n-1]范围内进行heapify,因为最后一个位置不考虑
}
}
/**
* 对index位置上的元素进行位置的调整,使得整个堆符合最大堆的规范
*
* ps:
* 这里的这个shitDown和之前的那个shitDown不同之处:
* 必须注意,两个shitDown不同的地方在于,这里的这个shitDown并不是对整个数组进行调整,而是对数组中我们指定的范围内的元素进行调整
* 我们之前写的那个shitDown是针对整个数组进行调整,所以两个shitDown对边界问题的处理不一样,必须得想明白。
* 弄清楚size的实际意义!
* */
private static void shitDown(int [] arr ,int index,int size) {
while((index*2+1) < size && index >= 0) {
int j = index*2+1;
if((j+1) < size && arr[j+1] > arr[j]) {
if(arr[index] > arr[j+1]) {
return;
}
j+=1;
}else{
if(arr[index] > arr[j]) {
return;
}
}
SortTestHelper.swap(arr, index, j);
index = j;
}
}
public static void main(String[] args) {
int testTime = 1000;
int maxValue = 100;
int maxSize = 10000;
boolean success = true ;
Heap heap = null;
for(int i = 0;i < testTime;i++) {
int [] arr1 = SortTestHelper.generateRandomArray(maxSize, maxValue);
int [] arr2 = SortTestHelper.copyArray(arr1);
int [] arr3 = SortTestHelper.copyArray(arr1);
SortTestHelper.comparator(arr2);
heapSort3(arr3);
if(!SortTestHelper.isEqual(arr2, arr3)) {
success = false;
SortTestHelper.printArray(arr1);
break;
}
}
if(success) {
System.out.println("Nice!!!");
}else {
System.out.println("Fuck!");
}
}
}
改进版代码:
package com.smarking.lzy.part2;
import com.smarking.lzy.part1.SortTestHelper;
/**
* 把原地堆排序重新写一下
* 巩固一下
* */
public class HeapSort4 {
public static void heapSort4(int [] arr) {
if(arr == null || arr.length == 0) {
return;
}
int count = arr.length;
for(int i = (count - 2)/2 ;i >= 0;i--) {
shitDown(arr,count,i);
}
for(int i = count - 1;i >= 1;i--) {
SortTestHelper.swap(arr, i, 0);
shitDown(arr,i,0);
}
}
private static void shitDown(int [] arr,int count,int index) {
while((index*2 + 1) < count && index >= 0) {
int j = index*2+1;
if((j+1) < count && arr[j+1] > arr[j]) {
j+=1;
}
if(arr[index] > arr[j]) {
return;
}
SortTestHelper.swap(arr, index, j);
index = j;
}
}
public static void main(String[] args) {
int testTime = 100000;
int maxValue = 100;
int maxSize = 10000;
boolean success = true ;
Heap heap = null;
for(int i = 0;i < testTime;i++) {
int [] arr1 = SortTestHelper.generateRandomArray(maxSize, maxValue);
int [] arr2 = SortTestHelper.copyArray(arr1);
int [] arr3 = SortTestHelper.copyArray(arr1);
SortTestHelper.comparator(arr2);
heapSort4(arr3);
if(!SortTestHelper.isEqual(arr2, arr3)) {
success = false;
SortTestHelper.printArray(arr1);
break;
}
}
if(success) {
System.out.println("Nice!!!");
}else {
System.out.println("Fuck!");
}
}
}