堆排序
堆排序是不稳定的排序算法在排序的过程中两个相等的元素的相对位置可能会发生改变,时间复杂度比较低为O(nlogn),空间复杂度为O(1)不需要额外的空间
堆排序借助了二叉堆这个数据结构,需要升序排序的时候使用最大堆,降序使用最小堆
升序排序步骤:
需要排序的无序数组可以看作是一个完全二叉树
- 首先将这个无序的完全二叉树,构建成一个最大堆
- 然后删除堆顶元素,具体的操作就是和堆的删除操作一样,先将堆顶元素和堆底元素交换然后删除堆顶元素,最后再调整堆也就是将交换后的堆顶元素下沉到合适位置,重复操作直到堆中元素删除完毕
详细代码如下:
// 堆排序(升序) 使用最大堆
public class HeapSort {
public static void main(String[] args) {
int []arr = {1,5,2,7,4,1000,0};
heapSort(arr);
System.out.println(Arrays.toString(arr));
}
//堆排序(升序) 首先构建最大堆 然后删除堆顶元素再调整堆为最大堆循环操作直到删除完毕
public static void heapSort(int []arr){
buildMaxHeap(arr); //构建最大堆
//删除堆顶元素 重新调整堆
for (int i = arr.length-1; i >0 ; i--) {
delMax(arr,i);
}
}
//构建最大堆 由于传入的数组可以看做是一个不满足二叉堆性质的完全二叉树 将其进行调整为二叉堆
//具体操作就是从第一个非叶子节点开始,遍历所有的非叶子节点进行下沉操作
public static void buildMaxHeap(int []arr){
for (int i = (arr.length-2)/2; i>=0; i--) {
sink(arr,i,arr.length-1);
}
}
public static void delMax(int []arr,int endIndex){
int temp = arr[0];
arr[0] = arr[endIndex];
arr[endIndex] = temp;
endIndex--;
sink(arr,0,endIndex);
}
//下沉操作
public static void sink(int[] arr,int p,int endIndex){
while(p*2+1<=endIndex){
int max = p*2+1;
if(p*2+2<=endIndex){
max = arr[max]<arr[p*2+2]?p*2+2:max;
}
if(arr[p]<arr[max]){
int temp = arr[p];
arr[p] = arr[max];
arr[max] = temp;
p = max;
}else{
break;
}
}
}
}
时间复杂度分析:
构建堆的过程中(n-2)/2*logn ,每次sink操作最坏是ogn的时间复杂度一共执行了(n-2)/2次
删除堆顶元素过程中:(n-1)*logn,每次sink操作最坏的时间复杂度也是logn一共执行了n-1次
构建堆和删除堆顶元素这两个操作是并列操作因此时间复杂度为O(nlogn)
空间复杂度:整个排序过程中没有使用到额外的存储空间因此空间复杂度为O(1)