堆排序用到的数据结构是完全二叉树,
完全二叉树要求,从上到下从左到右每一层节点多是满的,并且如果有未满层,则这个层所有元素都集中在最左边。
堆排序基本思想
(1)将待排序列构造成大顶堆,根据大顶堆性质,当前根节点(堆顶元素)就是当前序列的最大元素
(2)将当前序列的堆顶元素和最后一个元素调换位置,将剩下的元素重新构造成大顶堆。
(3)重复(2),反复操作,从第一次构造大顶堆开始每次构造大顶堆都会得到一个当前序列的最大元素,最终得到一个有序数列。
(4)如果构造的是大顶堆,得到的有序数列就是升序数列,小顶堆得到的有序数列就是降序数列
时间复杂度:O(nlogn)、空间复杂度:O(nlogn)
完全二叉树中父亲节点和孩子节点的关系
N(i)的父节点N[(i-1)/2]
N(i)的左孩子结点N(2i+1)
N(i)的右孩子结点N(2i+2)
排序过程
第一步:构建大顶堆
1、从第一个非叶子节点开始index=(arr.lengh-1)/2,判断他是否大于他的孩子结点,如果大于两个孩子结点无需交换,如果小于孩子结点,则将其与孩子节点交换
2、父亲节点向前移动,index=index-1。和他的两个孩子结点比较,小于孩子结点的话与孩子结点交换位置
3、父亲节点变成index=index-1=0。比较他和孩子结点大小,与大的孩子结点交换
4、交换后发现,结点2比他的孩子节点小,在对因为交换造成大顶堆结构破坏的节点重新进行构建。直到构建成完整的大顶堆。
5、将构建完整的大顶堆的堆顶元素的最后一个元素交换位置,在对剩下的元素重新构建大顶堆。重复此过程。
代码:
public class HeapSort {
public static void main(String[] args) {
int[] arr =new int[]{13,12,3,65,67,4,8,34,78,66};
heapSort(arr);
for(int e:arr){
System.out.println(e);
}
}
public static void heapSort(int[] arr){
//创建堆,
//从第一个非叶子结点开始,从右到左,从下到上开始
for(int i=(arr.length-1)/2;i>=0;i--){
adjustHeap(arr,i,arr.length);
}
//将堆的堆顶元素和最后一个元素交换
for(int i=arr.length-1;i>0;i--){
int temp=arr[0];
arr[0]=arr[i];
arr[i]=temp;
//调整后重新调整堆
adjustHeap(arr,0,i);
}
}
/**
* @Description: 调整堆结构
* @Param: [arr 待排数组, parent 父节点, length 待排数组长度]
* @return: [int[], int, int]
* @Author: Kwg
* @Date: 2022/1/6
*/
public static void adjustHeap(int[] arr,int parent,int length){
//临时变量存储父节点值
int temp =arr[parent];
//左孩子
int lChild=2*parent+1;
while(lChild<=length-1){
//让lCild指向最大的孩子
if(lChild+1<=length-1&&arr[lChild]<arr[lChild+1]){
lChild++;
}
//如果孩子节点小与父节点,直接结束
//这里判断的只能是temp不能是arr[parent],关系到循环的时候
if(arr[lChild]<=temp){
break;
}
//孩子结点大于父节点,将孩子结点的值赋给父节点
arr[parent]=arr[lChild];
//孩子结点和父节点交换之后,让左孩子当作父节点重新行下筛选,防止上次的交换破坏了下面的大顶堆结构
parent=lChild;
lChild=lChild*2+1;
}
arr[parent]=temp;
}
}