堆排序
在看堆排序之前,我们需要先了解堆结构和大顶堆
了解堆结构
简单来说,堆结构就是一个完全二叉树(要么为满二叉树、要么从左往右依次变满的二叉树)
存储形式为数组,heapsize为堆大小;数组下标从0开始,则堆的下标也亦如此,在堆结构中某i位置与该i位置上的左孩子、右孩子和父结点存在以下关系:
左孩子为2i+1;右孩子为2i+2;父结点为(i-1)/2
了解大顶堆:
对于每个父节点而言,它的左右子树都是小于父节点的;即大顶堆的第一个元素一定是最大值
堆排序思想(升序)
第一步
对于给定的待排序的数组,先将其数组全部用大顶堆表示;这样该数组的第一个元素就是整个数组的最大值;
实现大顶堆:依次遍历数组,将数组i位置上的数与其父结点((i-1)/2)做比较,大于父节点就做交换;
第二步
将堆的最后一个元素与第一个元素交换,堆的元素总个数减一;(数组最后一位排序完成即最大值)
第三步
执行完第二步后已经不是大顶堆,接下来要做的是将剩下的元素再次构成大顶堆;
思想:从该位置往下寻找它的左右孩子并从它左右孩子中找出最大值与它本身比较,如果它左右孩子中的最大一个大于它本身,那么就做交换;如此循环直至它没有左右孩子或者左右孩子没有比它大的就停止。
第四步
重复第二 三步,直至堆中元素的个数为0结束,数组升序完成;
代码
public static void heapSort(int[] arr){
if(arr==null||arr.length<2)
return;
for(int i=0;i<arr.length;i++){
heapInsert(arr,i);
}
int heapSize=arr.length;
swap(arr,0,--heapsize);
while(heapsize>0){
heapify(arr,0,heapsize);
swap(arr,0,--heapsize);
}
}
//将数组构成大顶堆
public static void heapInsert(int[] arr,int index){
while (arr[index]>arr[(index-1)/2]){//判断其与父结点位置的大小
swap(arr,index,(index-1)/2);
index=(index-1)/2;
}
}
//去掉最大值后,将剩下的堆再次构成大顶堆
public static void heapify(int[] arr,int index,int heapsize){
int left = index*2+1;//左孩子下标
while(left<heapsize){//下方还有孩子的时候
//俩个孩子中,谁的值大,把下标给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 static void swap(int[] arr,int i,int j){
int temp=arr[i];
arr[i]=arr[j];
arr[j]=temp;
}