1.什么是堆排序
堆排序是一种基于堆数据结构的排序算法,它的时间复杂度为O(nlogn),空间复杂度为O(1)。堆排序的基本思想是将待排序的序列构建成一个大根堆或小根堆,然后将堆顶元素与堆底元素交换,再对剩余的元素重新构建堆,重复以上步骤直到整个序列有序。堆排序是一种不稳定的排序算法,因为在构建堆的过程中,可能会改变相同元素的相对位置。
2.堆排序步骤
1.利用完全二叉树构建大顶堆
2.堆底堆顶进行交换,继续构建大顶堆,堆底不在参与构建
3. 不断重复2,直到变成有序数组
3.大顶堆
大顶堆是一种完全二叉树,其中每个节点的值都大于或等于其左右子节点的值。大顶堆的根节点是堆中的最大值。
如何构建大顶堆?
第一步:1.定义parent游标,从后往前遍历,指向第一个有孩子的节点
2.定义parent的左孩子child,(有孩子一定是左孩子)
3.判断parent有没有右孩子,如果有右孩子,左右孩子进行比较,child指向左右孩子当中的最大值
4.父子节点进行比较,如果父亲节点的值大,则parent继续向前移动
5.如果子节点大于父亲节点,则父子节点进行交换,parent指向child,child指向其左孩子,重复3-5,直到父节点的值大于子节点,或者没有孩子节点。
第二步:1.堆顶堆底进行交换,除堆底元素之外继续构建大顶堆
2. parent直接指向堆顶,定义parent的左孩子child,(有孩子一定是左孩子)
3.判断parent有没有右孩子,如果有右孩子,左右孩子进行比较,child指向左右孩子当中的最大值
4.父子节点进行比较,如果父亲节点的值大,则parent继续向前移动
5.如果子节点大于父亲节点,则父子节点进行交换,parent指向child,child指向其左孩子,重复3-5,直到父节点的值大于子节点,或者没有孩子节点。
在实际的代码操作中,不需要构建完全二叉树,直接在数组中找到父亲节点和左右孩子节点,arr[i]父亲节点为arr[(i-1)/2],arr[i]的左孩子arr[2i+1],arr[i]的右孩子arr[2i+2]。
3.代码实现
public static void main(String[] args) {
int[] arr = new int[]{2, 36, 78, 79, 76, 999, 890, 8, 3};
duipai(arr);
System.out.println(Arrays.toString(arr));
}
//堆排序
public static void duipai(int[] arr) {
//构建大顶堆
for (int p =(arr.length-2)/2; p>=0 ; p--) {
adjust(arr,p,arr.length);
}
//堆顶堆底进行交换,除堆底外剩余部分构建大顶堆
for (int i = arr.length-1;i >=0; i--) {
int temp=arr[i];
arr[i]=arr[0];
arr[0]=temp;
adjust(arr,0, i);
}
}
//维护
public static void adjust(int[] arr,int parent,int len){
int child=2*parent+1;
while (child<len){
//定义右孩子
int rchlid=2*parent+2;
if(rchlid<len&&arr[rchlid]>arr[child]){
//找到左右孩子当中的最大值
child=rchlid;
}
//父子节点比较
if(arr[parent]<arr[child]){
//如果父节点的值小
int temp=arr[parent];
arr[parent]=arr[child];
arr[child]=temp;
//父节点指向child,child继续向下指
parent=child;
child=2*child+1;
}else {
//父节点的值大
break;
}
}
}