堆排序详解及其代码实现

本文详细介绍了堆排序算法的原理和步骤,包括如何构建大顶堆以及如何完成整个排序过程。通过示例代码展示了如何在Java中实现堆排序,包括调整堆和交换堆顶堆底元素的函数。堆排序是一种高效的排序算法,适用于大规模数据的排序。

堆排序

1、整体思想

  • 利用完全二叉树构建大顶堆。
  • 完全二叉树必须从上到下,从左到右进行构建,这也保证了完全二叉树如果有孩子一定有左孩子
  • 大顶堆:父节点的值大于或等于其左右孩子的值
  • 堆顶元素和堆底元素进行互换,堆底元素不再参与构建
  • 重复上面的步骤

2、构建大顶堆

2.1、由数组构建完全二叉树

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-SRWNKkjF-1653145494300)(C:\Users\wu\Desktop\培训笔记\图片\QQ截图20220521211221.png)]

注意这个构建只是思路上的构建,实际上并不需要对数组做任何的操作。数组仍是那个数组,只是我们访问数组的形式发生了改变,我们把这个数组当成一颗完全二叉树然后用访问完全二叉树的方式访问数组。

2.2、由完全二叉树构建大顶堆

定义一个parent游标从后向前移动,直到遇到有孩子的节点
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-WMYS9fvk-1653145494302)(C:\Users\wu\Desktop\培训笔记\图片\QQ截图20220521213351.png)]

这实际上是在数组上进行移动

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-oAhbhAzx-1653145494304)(C:\Users\wu\Desktop\培训笔记\图片\QQ截图20220521213444.png)]

用child游标指向parent游标的孩子节点,由于是完全二叉树因此有孩子一定有左孩子

在数组中如果父节点的下标为parent,那左孩子的下标为2*parent+1,右孩子的下标为2*parent+2

如果有右孩子左右孩子进行对比,将child游标指向左右孩子中的最大值

父节点的值和子节点的值进行对比,如果子节点的值大于父节点的值,那么进行交换

交换后的子树可能会不符合大顶堆的定义

因此我们要检查以上面所述的子节点为根的子树是否符合大顶堆的定义

具体的操作如下:将parent游标指向如上所述的子节点parent=child。child游标指向其孩子既是child=2*parent+1。

再重复上述步骤

这样大顶堆就构建完成了

构建大顶堆的代码如下

public class HeapSort {
    public static void main(String[] args) {
        int[] arr={22,34,54,1,655,3423,12};
        for(int p=arr.length-1;p>=0;p--){
            adjust(arr,p,arr.length);
        }
    }
    public static void adjust(int[] arr,int parent,int length ){
        int child=2*parent+1;
        while(child<length){
            int rchild=child+1;
            if(rchild<length&&arr[rchild]>arr[child]){
                child++;
            }
            if(arr[parent] < arr[child]){
                int temp = arr[parent];
                arr[parent] = arr[child];
                arr[child] = temp;
                parent = child;
                child = 2 * child + 1;
            }else {
                break;
            }
        }
    }
}

3、完成堆排序

构建完大顶堆后根节点的值一定是最大的,上面的数组构建为大顶堆后如下

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-sAt2mY9G-1653145494304)(C:\Users\wu\Desktop\培训笔记\图片\QQ截图20220521222650.png)]

将堆顶和堆底互换,此时最大数放在了数组的最后一个位置。再次操作时忽略最后一个元素,因为该元素已经放在正确的位置了。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Au5BeomB-1653145494305)(C:\Users\wu\Desktop\培训笔记\图片\QQ截图20220521223105.png)]

忽略最后一个元素得到:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-VAzO3zUQ-1653145494306)(C:\Users\wu\Desktop\培训笔记\图片\QQ截图20220521223242.png)]

由于只是交换了堆顶和堆底的元素,而且堆底的元素已经被忽略,所以此时能破坏堆结构的数只能是堆顶的元素,所以我们应该从堆顶(也就是数组的第一个元素)像上面构建大顶堆一样对堆进行调整。

整体代码如下

public class HeapSort {
    public static void main(String[] args) {
        int[] arr={22,34,54,1,655,3423,12};
        //下面的循环是在构建大顶堆,因此必须对每个父节点进行调整使得父节点大于子节点
        for(int p=arr.length-1;p>=0;p--){
            adjust(arr,p,arr.length);
        }
        //i每次减一表示忽略堆底的元素
        //下面的循环是为了交换堆底堆顶元素,在对交换完后的堆进行调整从而恢复成大顶堆
        //由于只是交换了堆顶和堆底的元素,而且堆底的元素已经被忽略,所以此时能破坏堆结构的数只能是堆顶的元素,所以我们应该从堆顶(也就是数组的第一个元素)像上面构建大顶堆一样对堆进行调整。
        for(int i = arr.length -1;i>=0;i--){
            //交换堆顶与堆底的元素
            int temp = arr[i];
            arr[i] = arr[0];
            arr[0] = temp;
            adjust(arr,0,i);
        }
        System.out.println(Arrays.toString(arr));
    }
    public static void adjust(int[] arr,int parent,int length ){
        int child=2*parent+1;
        while(child<length){
            int rchild=child+1;
            if(rchild<length&&arr[rchild]>arr[child]){
                child++;
            }
            if(arr[parent] < arr[child]){
                int temp = arr[parent];
                arr[parent] = arr[child];
                arr[child] = temp;
                parent = child;
                child = 2 * child + 1;
            }else {
                break;
            }
        }
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值