堆排序
首先了解一下什么是堆排序?
堆排序(英语:Heapsort)是指利用堆这种数据结构所设计的一种排序算法。堆是一个近似完全二叉树的结构,并同时满足堆的性质:即子结点的键值或索引总是小于(或者大于)它的父节点。其复杂度为0(nlogn),有两种形式:
大顶堆:
小顶堆:
如图所示,大顶堆的父节点要比其左右两个子节点的值都要大;同理,小顶堆的父节点要比其左右两个子节点的值都要小。
不论是大顶堆还是小顶堆,其左右(孩子)节点都是2i+1(左)、2i+2(右),其中i表示父节点的下标。
算法思路:
将无序数组进行堆化,构建小顶堆或大顶堆(一般升序采用大顶堆,降序采用小顶堆)。从第一个非叶子节点开始(叶结点自然不用调整,第一个非叶子结点 n/2-1,n是数组长度)分别比较该父节点的左右节点,若该父节点小于子节点则进行交换,交换后的子节点数值发生变化可能会不满足堆的性质,需重新调整二叉树的结构
初始堆完成以后,将根节点与最后一个节点交换(以大顶堆为例)交换后,此时最后一个元素为最大的值,不满足大顶堆的性质,对其进行向下调整,重新调整为大顶堆的形式,再将堆顶元素与末尾元素交换,得到第二大元素。如此反复进行交换、重建、交换。
简单来说就三个步骤:
- 对无序数组进行初始堆化
- 将根节点与最后一个节点交换,将最大元素“沉”到末端
- 重新调整堆的结构,然后将根节点与倒数第二个节点交换,将第二大元素“沉”到末端,以此类推重复上述过程,将堆变成有序即可。
话不多说,上代码: (温馨提示,没写主方法,需要自己调用测试)
static void sort(int[]nums){
Int n=nums.length;
//对数组进行堆化
Maxheap(nums,n);
//把最后一个元素和堆顶进行对调
int i;
for (i = n-1; i >=0; i--) {
swap(nums,0,i);
//缩小堆的范围对堆顶进行向下调整
MaxheapFixedDown(nums,0,i);
}
}
static void swap(int[]nums,int p,int r){
int temp=nums[p];
nums[p]=nums[r];
nums[r]=temp;
}
若将数组化为大顶堆:(升序)
static void Maxheap(int[]nums){
Int n = nums.length;
for (int i = n/2-1; i >=0; i--) {
MaxheapFixedDown(nums,i,n);
}
}
static void MaxheapFixedDown(int[]nums,int i,int n){
//找到父节点的左右两个孩子
int left=2*i+1;
int right=2*i+2;
//比较孩子大小,如果父节点最大则不用交换
if(left>=n){
return;
}
int max=left;
if(right>=n){
max=left;
}else if(nums[left]<=nums[right]){
max=right;
}
if(nums[i]>=nums[max])return;
//否则将两个孩子中最大的与父元素交换
swap(nums,i,max);
//重新调整二叉树的顺序,变成大顶堆(顺序)
MaxheapFixedDown(nums,max,n);
}
若将数组化为小顶堆:(降序)
static void Minheap(int[]nums){
int n=nums.length;
for (int i = n/2-1; i >=0; i--) {
MinheapFixedDown(nums,i,n);
}
}
static void MinheapFixedDown(int[]nums,int i,int n){
//找到父节点的左右两个孩子
int left=2*i+1;
int right=2*i+2;
//比较孩子大小,如果父节点最小则不用交换
if(left>=n){
return;
}
int min=left;
if(right>=n){
min=left;
}else if(nums[left]>=nums[right]){
min=right;
}
if(nums[i]<=nums[min])return;
//否则将两个孩子中最小的与父元素交换
swap(nums,i,min);
//min的位置发生改变,重新调整二叉树的顺序,变成小顶堆(逆序)
MinheapFixedDown(nums,min,n);
}
so easy too happy!👀