经典算法(16)堆排序图解及其代码实现

写在前面: 我是 扬帆向海,这个昵称来源于我的名字以及女朋友的名字。我热爱技术、热爱开源、热爱编程。技术是开源的、知识是共享的

这博客是对自己学习的一点点总结及记录,如果您对 Java算法 感兴趣,可以关注我的动态,我们一起学习。

用知识改变命运,让我们的家人过上更好的生活

关于快速排序的算法参考 经典算法(4)一文搞懂什么是 快速排序;关于二分法的算法可以参考另一篇博客 经典算法(2)一文搞懂二分法查找(循环和递归两种方式); 有关冒泡排序的算法参考 经典算法(1)冒泡排序及其优化

一、堆排序介绍

百度百科是这样介绍堆排序的:
在这里插入图片描述

二、堆排序的算法思想

  • 构造一个大根堆
    将堆的末端子结点进行调整,使得子结点永远小于父结点;整个序列的最大值就是堆顶的根结点。即每个结点的值都大于其左孩子和右孩子结点的值。

  • 堆排序
    移除位在第一个数据的根结点,将其与末尾元素进行交换,末尾就是最大值;
    将剩余n-1个元素重构造成一个堆,这样就会得到n个元素的次大值,反复 执行,就得到一个有序序列

三、堆排序的过程图解

以 30,60,0,50,80,20,10 为例进行说明

第一步:把放在数组里的元素看成一个二叉树,对该二叉树进行调整,使之变成一个大根堆。

思路: 从最后一个非叶子结点开始,从左到右,从下到上进行调整
在这里插入图片描述
经过上述步骤,我们得到了一个大根堆
在这里插入图片描述
第二步:将堆顶元素与与最后一个数交换,将最大元素下沉到数组末尾,然后将剩余的数再构造成一个大根堆。如此循环反复执行

第1次:删除结点80,将80与10进行交换
在这里插入图片描述

第2次:删除结点60,将60与0进行交换
在这里插入图片描述

第3次:删除结点50,将50与0进行交换
在这里插入图片描述
第4次:删除结点30,将30与0进行交换
在这里插入图片描述
第5次:删除结点20,将20与0进行交换
在这里插入图片描述
第6次:删除结点10,将10与0进行交换
在这里插入图片描述

四、代码实现

public class HeapSort {

    public static void main(String[] args) {
        System.out.println("输入要排序的值,输入的每个值用逗号隔开:");
        Scanner input = new Scanner(System.in);
        String str = input.nextLine();
        // 将字符串按照","拆分成字符串数组
        String[] strArray = str.split(",");
        // 新建数组用来存储拆分出来的每个值
        int[] array = new int[strArray.length];
        // 给数组循环遍历赋值
        for (int i = 0; i < strArray.length; i++) {
            array[i] = Integer.parseInt(strArray[i]);
        }

        System.out.println("排序前的数组:" + Arrays.toString(array));
        // 排序
        heapSort(array);
        System.out.println("排序后的数组:" + Arrays.toString(array));
    }

    /**
     * 堆排序
     *
     * @param array 需要进行排序的数组
     */
    private static void heapSort(int[] array) {
        // 把无序的数组构建成大根堆(每个结点的值都大于其左孩子和右孩子结点的值)
        for (int i = array.length / 2 - 1; i >= 0; i--) {
            downAdjust(array, i, array.length);
        }
        System.out.println("调整以后的堆数组: " + Arrays.toString(array));
        // 循环将堆顶元素与末尾元素交换,将最大元素下沉到末端,重新调整堆,产生新的堆顶。直到整个数组有序
        for (int i = array.length - 1; i > 0; i--) {
            // 最后一个元素与第一个元素进行交换
            int temp = array[i];
            array[i] = array[0];
            array[0] = temp;
            // 下沉调整最大堆
            downAdjust(array, 0, i);
        }
    }

    /**
     * 将无序数组构造成一个大根堆
     *
     * @param array       待调整的堆数组
     * @param parentIndex 父结点的索引
     * @param length      需要调整的堆数组的大小
     */
    private static void downAdjust(int[] array, int parentIndex, int length) {

        // 取出父结点的值,保存在临时变量
        int temp = array[parentIndex];
        // childIndex = 2 * parentIndex + 1 是parentIndex结点的左孩子结点
        for (int childIndex = 2 * parentIndex + 1; childIndex < length; childIndex = 2 * childIndex + 1) {
            if (childIndex + 1 < length && array[childIndex] < array[childIndex + 1]) {
                // childIndex 指向右孩子结点
                childIndex++;
            }
            // 如果子结点值大于父结点的值,把较大的值赋值给当前结点
            if (array[childIndex] > temp) {
                array[parentIndex] = array[childIndex];
                parentIndex = childIndex;
            } else {
                break;
            }
        }
        // 当循环结束以后,以parentIndex为父结点的树的最大值,放在了最顶端
        array[parentIndex] = temp;
    }

}

代码执行结果:
在这里插入图片描述


由于水平有限,本博客难免有不足,恳请各位大佬不吝赐教!

发布了81 篇原创文章 · 获赞 1443 · 访问量 20万+
展开阅读全文

没有更多推荐了,返回首页

©️2019 CSDN 皮肤主题: 鲸 设计师: meimeiellie

分享到微信朋友圈

×

扫一扫,手机浏览