1、 堆排序简介
-
堆是一个完全二叉树,堆的时间复杂度是O(nlogn),空间复杂度是O(1)。
-
堆是一个不稳定的排序算法,即在两个相同值的数据在交换后相对位置可能发生改变。如下:
-
堆分为大顶堆和小顶堆
大顶堆:每个结点的值都大于其左孩子和右孩子结点的值
小顶堆:每个结点的值都小于其左孩子和右孩子结点的值
2、 堆的结构和构造流程
-
堆节点之间的关系
将数组元素按从左到右,从上到下的顺序排列,构造成树。节点之间的关系便满足:
父结点索引:(i-1)/2
左孩子索引:2i+1
右孩子索引:2i+2
这是数组以另一种形式展示,便于构造堆。
-
构成大顶堆的过程如下:从第一个数组元素开始,依次插入,最终构成大顶堆。
-
构成大顶堆后,将堆顶的元素与最后一个元素交换位置。即8和2交换,8的位置便永久固定。数组的长度减1。此时新的堆不是大顶堆的样式,从堆顶元素2开始向下比较,重新构建大顶堆。
主要步骤:
(1)找出左右孩子值较大的节点 largerIndex,比较父节点和 largerIndex,若largerIndex的值大,则交换父节点和largerIndex。
(2)然后比较largerIndex和它的孩子节点中拥有较大值的节点。
(3)一直向下比较,直到到达最底层。
例如上图,比较 2 和 7,7比2 大,交换2和7的位置。
比较2和6,6比2大,交换2和6的位置。
此时构成大顶堆,如下图所示。
-
大顶堆构建成功,继续交换7和1,然后7固定,数组长度继续减1。
-
重复上述过程,即每一次将最大的元素放到了数组尾部,最终数组元素排列成递增顺序。
注意由上图可知,在重新构造堆的过程,两个相同的数,如红6和绿6的先后顺序可能会发生改变。如红6和绿6同时作为堆顶的孩子节点,则在找两者之间较大值的时候,两者的顺序是不一定的。如下图
算法
import java.util.Arrays;
/**
* @author lx
* @version 1.0
* @description: 堆排序
* @date 2021/8/16 22:01
*/
public class HeapSort {
//测试
public static void main(String[] args) {
int[] nums = {3,6,8,5,7};
heapSort(nums);
}
//堆排序
public static void heapSort(int[] arr){
//构造大顶堆
heapCreate(arr);
int size = arr.length;
while(size>1){
//交换堆顶元素和末尾元素
swap(arr,0,size-1);
//数组长度减1
size--;
//重新构造大顶堆
rebuild(arr,0,size);
}
System.out.println(Arrays.toString(arr));
}
//构造大顶堆
public static void heapCreate(int[] arr){
for (int i = 0; i < arr.length; i++) {
//当前的索引值
int cur = i;
//父节点索引
int fath = (cur-1)/2;
//将新插入的值与其父结点比较,逐级向上,放在最终位置上
while (arr[cur]>arr[fath]){
//交换当前节点与父节点的顺序
swap(arr,cur,fath);
//将当前索引指向父索引
cur = fath;
//父索引改变
fath = (cur-1)/2;
}
}
}
//堆顶元素与最后一个元素交换位置后,重新构建大顶堆
//从上到下的比较过程
public static void rebuild(int[] arr,int index,int size){
int left = index * 2 + 1;
int right = index * 2 + 2;
while (left<size){
//找出左右孩子节点中值最大的索引
int largerIndex;
if(arr[left]<arr[right]&&right<size){
largerIndex = right;
}else {
largerIndex = left;
}
//比较父节点和孩子中较大的值,确定最大值的索引
if(arr[index]>arr[largerIndex]){
largerIndex = index;
}
//如果父节点比左右孩子节点的值大,则退出循环
if(index==largerIndex){
break;
}
//交换父节点和子节点较大的值
swap(arr,largerIndex,index);
//向下走一级
index = largerIndex;
left = index * 2 + 1;
right = index * 2 + 2;
}
}
//交换数组中两个元素的值
public static void swap(int[] arr, int i, int j) {
int temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
}