堆排序(JAVA)

堆排序就是利用堆的特性来进行排序的一种算法,在学习堆排序之前需要先了解什么是堆,以及什么是大根堆、小根堆。

        堆:堆就是一颗完全二叉树。

        大根堆:堆中的每一个节点都大于等于它的孩子节点。

        小根堆:堆中的每一个节点都小于等于它的孩子节点。

在了解了堆之后,我们还需要知道堆在数组中是如何存放的,如图:

由此可以知道,对于堆中的任意一个节点 i:

i 的左孩子:2*i + 1

i 的右孩子:2*i + 2

i 的父节点:(i - 1) / 2

堆排序的实现可以分为对两个问题的解决:

        1. 对于任意的一个数组,如何将数组调整成为一个大根堆?

        实现思路:遍历数组,将每一个元素与自己的父节点比较,如果大于父节点,则与父结点交换位置,然后继续与交换之后新的父节点比较,如果依然大于新的父节点,就继续交换比较,直到不再大于自己的父节点或成为根节点。

        Java代码:

public class heapInsert {

    public static void heapinsert(int[] arr){
        for(int i = 0; i < arr.length; i++){
            int heap = i;
            //当前节点大于自己的父节点时,与父结点交换位置
            while(arr[heap] > arr[(heap - 1) / 2]){
                swap(arr, heap, ((heap - 1) / 2));
                heap = (heap - 1) /2;
            }
        }
    }
    //交换两个元素在数组中的位置
    public static void swap(int[] arr, int a, int b){
        int temp = arr[a];
        arr[a] = arr[b];
        arr[b] = temp;
    }

    public static void main(String[] args){
        int[] arr = {4,2,7,9,1,6,4,8,2,4,5,1};
        heapinsert(arr);
        System.out.println(Arrays.toString(arr));
    }
}

        2. 对于一个大根堆,将最大值放到最后,再把除最大值以外剩下的元素重新调整为大根堆

        实现思路:对于一个大根堆,根位置的值即为最大值,首先将根位置的元素与最后一位的元素交换,然后将新的根位置的元素与自己的最大孩子节点比较,如果小于自己的最大孩子,就与之交换,并继续和新的最大孩子比较,直到大于自己的最大孩子。

        Java代码:

public class heapIfy {

    public static void heapify(int[] arr){
        int heapSize = arr.length - 1;
        //将最后一位放到原来的根的位置
        swap(arr, heapSize, 0);
        //标记此时在根位置上的元素
        int n = 0;
        //如果n还有孩子节点
        while (n * 2 + 1 < heapSize) {
            //如果存在右孩子并且右孩子大于左孩子,max = 右孩子的下标,否则等于左孩子的下标
            int max = n * 2 + 2 < heapSize && arr[n * 2 + 2] > arr[n * 2 + 1] ? n * 2 + 2 : n * 2 + 1;

            //如果n位置的值大于等于max位置的值,那么max更新为n
            max = arr[n] >= arr[max] ? n : max;

            //如果最终的max就等于n,说明n已经是比自己的两个孩子都大,此时已经是大根堆,不用继续比较
            if (max == n) {
                break;
            }
            //将左右孩子中最大的与父节点交换,交换之后更新n,继续比较
            swap(arr, n, max);
            n = max;
        }

    }
    //交换数组中的两个数
    public static void swap(int[] arr, int a, int b){
        int temp = arr[a];
        arr[a] = arr[b];
        arr[b] = temp;
    }
    //主方法
    public static void main(String[] args){
        int[] arr = {9, 8, 6, 7, 5, 4, 4, 2, 2, 1, 4, 1};
        heapify(arr);
        System.out.println(Arrays.toString(arr));

    }
}

综合以上两个问题,完整代码如下:

public class heapSort {

    public static void heapInsert(int[] arr){
        for(int i = 0; i < arr.length; i++){
            int heap = i;
            //当前节点大于自己的父节点时,与父结点交换位置
            while(arr[heap] > arr[(heap - 1) / 2]){
                swap(arr, heap, ((heap - 1) / 2));
                heap = (heap - 1) /2;
            }
        }
    }

    public static void heapIfy(int[] arr) {
        for (int heapSize = arr.length - 1; heapSize > 1; heapSize--) {
            //将最后一位与原来的根交换位置
            swap(arr, heapSize, 0);
            //标记此时在根位置上的元素
            int n = 0;

            //如果n还有孩子节点
            while (n * 2 + 1 < heapSize) {
                //如果存在右孩子并且右孩子大于左孩子,max = 右孩子的下标,否则等于左孩子的下标
                int max = n * 2 + 2 < heapSize && arr[n * 2 + 2] > arr[n * 2 + 1] ? n * 2 + 2 : n * 2 + 1;

                //如果n位置的值大于等于max位置的值,那么max更新为n
                max = arr[n] >= arr[max] ? n : max;

                //如果最终的max就等于n,说明n已经是比自己的两个孩子都大,此时已经是大根堆,不用继续比较
                if (max == n) {
                    break;
                }
                //将左右孩子中最大的与父节点交换,交换之后更新n,继续比较
                swap(arr, n, max);
                n = max;
            }
        }
    }

    //交换数组中的两个数
    public static void swap(int[] arr, int a, int b){
        int temp = arr[a];
        arr[a] = arr[b];
        arr[b] = temp;
    }

    public static void main(String[] args){
        int[] arr = {4,2,7,9,1,6,4,8,2,4,5,1};
        heapInsert(arr);
        heapIfy(arr);
        System.out.println(Arrays.toString(arr));
    }
}

以上内容为个人学习总结,如有错误,欢迎指正。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值