最近看了一下堆排序,整体来说,其实还是有些难度了,这里以大根堆为例,这里我将堆排序分成了二步:
第一步:将原数组变为一个大根堆(大根堆的特点:树的根节点永远大于子节点)
第二步:摘掉第一个元素(大根堆的根节点),使剩下的元素继续变为大根堆
现在我们考虑如何将原来的数组变为大根堆呢?
首先知道节点的父节点的位置
当前节点:index
父节点:(index-1)/2
想想如果当前节点小于父节点,是不是应该将两个节点进行交换,交换完后,当前节点的索引变为交换后位置的索引,继续找其父节点进行比较,直到他比父节点小,退出循环,index从0~len-1后,整个数组就变为了大根堆,废话不多说,看代码:
//index索引从0开始
private static void heapInsert(int[] arr, int index) {
//当前节点大于父节点,进行交换
while(arr[index] >arr[(index -1)/2]){
swap(arr,index,(index-1)/2);
index = (index-1)/2;
}
}
private static void swap(int[] arr, int i, int j) {
int temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
现在变为了大根堆,那么根节点肯定是最大的,这个时候我们把根节点和最后一个位置元素(heapSize-1)进行交换,heapSize--,即排序了一个最大元素,前面的元素必然不再是大根堆,(因为根节点必然小于左右节点,这时候我们就要找到它该待的位置)
当前节点:idnex
左节点:index * 2 +1
右节点:index* 2 +2
将当前节点和左右节点比较,情况如下:
private static void heapify(int[] arr, int index, int heapSize) {
//找左孩子的下标
int left = index * 2 + 1;
//如果左孩子的下标 < heapSize,说明有孩子
while(left < heapSize){
//left +1 < heapSize,说明有有右孩子,找左右孩子的大的下标
int largest = left +1 < heapSize && arr[left] < arr[left + 1]?left+1:left;
//比较当前节点和孩子中最大的元素,选取下标
largest = arr[index] > arr[largest]?index:largest;
//如果最大就是自己,已经是堆了
if(largest == index){
break;
}
//如果不是,交换,继续和下一层的孩子比较
swap(arr,index,largest);
index = largest;
left = index * 2+ 1;
}
}
进行完堆化操作后,此时根节点又是最大的一个元素,与heapSize-1的位置元素交换,heapSize--
循环堆化while(heapSize-1>0),取第一个元素和heapSize-1位置交换,最后整个数组有序
整个流程代码如下:
public static void main(String[] args) throws InterruptedException {
int[] arr = new int[]{3,6,9,8,4,5,7,1,2};
heapSort(arr);
print(arr);
}
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);
}
}
private static void heapify(int[] arr, int index, int heapSize) {
//找左孩子的下标
int left = index * 2 + 1;
//如果左孩子的下标 < heapSize,说明有孩子
while(left < heapSize){
//找左右孩子的大的下标
int largest = left +1 < heapSize && arr[left] < arr[left + 1]?left+1:left;
//比较当前节点和孩子中最大的元素,选取下标
largest = arr[index] > arr[largest]?index:largest;
//如果最大就是自己,已经是堆了
if(largest == index){
break;
}
//如果不是,交换,继续和下一层的孩子比较
swap(arr,index,largest);
index = largest;
left = index * 2+ 1;
}
}
private static void heapInsert(int[] arr, int index) {
//当前节点大于父节点,进行交换
while(arr[index] >arr[(index -1)/2]){
swap(arr,index,(index-1)/2);
index = (index-1)/2;
}
}
private static void swap(int[] arr, int i, int j) {
int temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
private static void print(int[] arr) {
for(int i : arr){
System.out.print(i + " ");
}
}
有错误的地方或说的不明白的地方,希望谅解一下,本人也是个小白!