堆排序原理
堆排序是利用堆这种数据结构而设计的一种排序算法,堆排序是一种选择排序,它的平均时间复杂度均为O(nlogn)。
堆排序的基本思路是:
- 将待排序的序列构造成一个大顶堆,此时,整个序列的最大值就是堆顶的根节点。
- 将根节点与末尾元素进行交换,此时末尾就为最大值。
- 然后将剩余N-1个元素重新构成一个堆,这样根节点就是这n个元素的次大值。
- 将根节点与倒数第二个元素交换。
- 如此反复执行,最后得到一个从小到大的序列。
简化的思路:
- 将无序数组构建成一个堆,根据升序降序需求选择大顶堆或小顶堆;
- 将堆顶元素与末尾元素交换,将最大元素“沉”到数组末端;
- .其余元素重新调整结构,使其满足堆定义,然后继续交换堆顶元素与当前末尾元素;
- 反复执行调整+交换步骤,直到整个序列有序。
以下面的数组排序为例:
首先构造初始堆
从最后一个非叶子几点开始(叶子节点自然不用调整,最后一个非叶子节点索引为arr.length/2-1),从右至左,从下至上进行向下调整,使其满足堆的定义。
步骤二:将堆顶元素与末尾元素进行交换,使末尾元素最大。
其余元素恢复大顶堆
再把堆顶元素下移至倒数第二个位置
如此反复进行,最终使得整个序列有序
Java实现堆排序
package heap;
import java.util.Arrays;
public class HeapSort {
/**
* 测试方法
*/
public static void main(String[] args) {
int[] arr = {1,4,2,6,3,44,62,31,8,7,5};
sort(arr);
System.out.println(Arrays.toString(arr));
}
/**
* 堆排序的实现方法
*/
public static void sort(int[] arr){
//首先把arr调整为一个合法的堆
for (int i= arr.length/2-1;i>=0;i--){
//从最后一个非叶子节点开始调整,直到根节点
//具体的调整封装为一个方法
adjustHeap(arr,i,arr.length);
}
//
for (int j=arr.length-1;j>0;j--){
//交换堆顶元素和堆尾元素的位置
swap(arr,0,j);
//把除了我们刻意后置的元素外的剩余元素,调整为合法的堆
adjustHeap(arr,0,j);//由于我们只移动了第一个元素,所以只需要调整第一个元素
}
}
/**
* 调整的方法
* @param arr 数组
* @param i 调整元素的索引
* @param length 调整数组的前length个元素
*/
public static void adjustHeap(int[] arr, int i, int length){
int temp = arr[i];
for (int k=i*2+1;k<length;k=k*2+1){
if (k+1<length && arr[k]<arr[k+1]){
//存在右子节点
k++; //让k指向两个子节点中大的那个
}
if (temp>=arr[k]){
//父节点大于子节点中最大的那个值
break;
}else {
//调整
arr[i] = arr[k];
i=k;
}
}
//现在i的位置就是被调整的节点应该放的正确位置
arr[i] = temp;
}
/**
* 交换a,b两个元素的方法
* @param arr 数组
* @param a 元素a的索引
* @param b 元素b的索引
*/
public static void swap(int[] arr, int a, int b){
int temp = arr[a];
arr[a] = arr[b];
arr[b] = temp;
}
}