算法之堆排序

在此次学习的过程中,有个数学问题一直困扰着我。
就是为什么2^0+2^1+2^2=2^3-1。网上找的答案说是这个

S=2^0+2^1+2^2+2^3+.+2^(n-1)
2S=2^1+2^2+2^3+...+2^(n-1)+2^n
两式相减,
2S-S=2^n-2^0
S=2^(n)-1

但是这只是推导过程,还是没说为什么会这样。
然后我仔细想了想,想出来了哈哈哈!!!
首先2^0+2^1+2^2=2^3-1 可以变为2^0+2^1+2^2=2^3-2^0 ,然后移项,变为
2^0+2^0+2^1+2^2=2^3 ,因为2^0+2^0=2^1 ,所以公式变为2^1+2^1+2^2=2^3 ,就等于2^2+2^2=2^3就等于2^3=2^3,推到完成!!麻蛋初中的知识都忘了,哼唧~


在学习堆排序之前有必要了解一下完全二叉树的结构原理,因为堆排序是基于完全二叉树的。
https://blog.csdn.net/qq_22642239/article/details/80774013 可以参考下这篇文章学习一下树。

一,先说二叉树:
名词解释:
1.层次:顶级节点是0层,依次往下递增。
2.高度:指顶级节点到最远子节点的边长。什么都没有树高为-1;顶级节点树高为0。
比如如下图(右),为二叉树,左为完全二叉树:
一共有3个层次;树高就是最长的边,树高为3.
完美二叉树完美二叉树(左)二叉树二叉树(右)

二叉树特点:
(1)若二叉树的层次从0开始,则在二叉树的第i层至多有2^i个结点(i>=0)。

(2)高度为k的二叉树最多有2^(k+1) - 1个结点(k>=-1)。 (空树的高度为-1)

(3)对任何一棵二叉树,如果其叶子结点(度为0)数为m, 度为2的结点数为n, 则m = n + 1。

上面所说最多的意思就是指代完美二叉树。
我们现在拿完美二叉树推导二叉树的特性为什么是这样。
1.特点三:完美二叉树每一层结点个数为2^0,2^1,2^2,2^3.......2^n.,自己可以看图参考下,因为我们最上面已经证明了2^0+2^1+2^2=2^3-1,所以对于当二叉树为完美二叉树时,特点三可以这样理解,看图完美二叉树,叶子结点(度为0)m=2^3,度为2的结点数n为2^0+2^1+2^2,就是所有非叶子结点数量.因为2^0+2^1+2^2=2^3-1,即n=m-1,也就是m=n+1.以此类推可以得到完美二叉树都符合这个特性。
2.特点二:依然符合我们的2^0+2^1+2^2=2^3-1公式,看这句话:高度为k的完美二叉树的结点数是高度为k+1的完美二叉树的叶子节点数-1.看图,完美二叉树,前三排的结点总数,是第四排的节点数-1;我们虚构一个第五排,那前四排的结点总数是第五排的节点数-1。所以,高度为k的完美二叉树最多有2^(k+1) - 1个结点。
3.特点一:这个就简单了,完美二叉树每层结点都是2^n-1.

以上用完美二叉树,推导二叉树的特性,属于特殊情况,二叉树的推导,以后有时间再看吧。

本次学习的猪脚来了:完全二叉树!
在这里插入图片描述
完全二叉树从根结点到倒数第二层满足完美二叉树,最后一层可以不完全填充,其叶子结点都靠左对齐。
copy~:
堆排序是将数据看成是完全二叉树、根据完全二叉树的特性来进行排序的一种算法

最大堆要求节点的元素都要不小于其孩子,最小堆要求节点元素都不大于其左右孩子
那么处于最大堆的根节点的元素一定是这个堆中的最大值

完全二叉树有个特性:左边子节点位置 = 当前父节点的两倍 + 1,右边子节点位置 = 当前父节点的两倍 + 2

那么对一个数组而言,如果把数组看做逻辑上的堆,最后一个父结点下标就是arr.length/2-1,为什么呢?
解释:设最后一个父结点在数组中的位置为i,
则如果只有一个子节点,则子节点为左子节点,左子节点为:2i+1,左子节点在数组中最后一个位置,则:2i+1=arr.length-1;
如果有父结点有两个子节点,最后一个子节点为右子结点:2i+2,右子结点在数组中最后一个位置,则:2i+2=arr.length-1;
两个公式移项得:i=arr.length/2-1;或者i=arr.length/2-3/2;3/2向下取整为1,所以也是i=arr.length/2-1。
下面看代码:

    public static void main(String[] args) {
        int[] arr = new int[10];  //构建一个空的一维数组
        for (int i = 0; i < arr.length; i++) {
            int temp = (int) (Math.random() * 1000) + 1;//随机产生一个 1~10 的整数
            arr[i] = temp;//将产生的数添加到数组
        }
        long a = System.currentTimeMillis();
        heapSort(arr);
        long b = System.currentTimeMillis();
        System.out.println(a - b + "毫秒");
        for (int i = 0; i < arr.length; i++) {
            System.out.println(arr[i]);
        }
    }

    public static int[] heapSort(int[] arr) {
        // 调整堆,从最开始的三个数形成的小堆,一直扩展到整个数组形成的大堆。 这是自底向上构建堆
        for (int i = arr.length / 2 - 1; i >= 0; i--) {
            adjustHeap(arr, i, arr.length);
        }
        //排序
        for (int j = arr.length - 1; j > 0; j--) {
            //把数组起始位置的数放到最后,因为经过调整堆之后,数组下标为0的数据是最大的,把最大的数据放在数组最后面。
            swap(arr, 0, j);
            // 数组交换位置后,堆得结构需要重新调整,使得下一个最大值存放到数组起始位置,这是自顶向下调整堆,最多比较次数是树高
            adjustHeap(arr, 0, j);
        }
        return arr;
    }

    /**
     * 交换元素
     *
     * @param arr
     * @param a   元素的下标
     * @param b   元素的下标
     */
    public static void swap(int[] arr, int a, int b) {
        int temp = arr[a];
        arr[a] = arr[b];
        arr[b] = temp;
    }

    // 调整堆
    public static void adjustHeap(int[] array, int i, int length) {
        int temp = array[i];
        // 这一步的意思是判断当前结点是否有左子节点,k=2*k+1的意思是如果左子结点下面还有左子节点,那么继续进行判断比较是否符合堆规则
        for (int k = 2 * i + 1; k < length; k = 2 * k + 1) {
            // 这一步的意思是判断是否有右子结点,并且右子结点数据比左子节点大,则取右子结点
            if (k + 1 < length && array[k] < array[k + 1]) {
                k++;
            }
            // 取得左右子结点中较大的那一个和父节点比较,如果比父节点大,则交换
            if (array[k] > temp) {
                // 交换三角堆中数据,大的值作为父节点
                swap(array, i, k);
                // 这一部很重要,我就卡着了,顺便吐槽一下百度百科的java堆排序,没有这一步,那个排序是错的
                //这步的意义就是:三角堆中,子结点和父结点的数据交换后,判断新形成的三角堆,以子结点为父结点的下方的三角堆是否满足大顶堆,调整。
                i = k;
            } else {
                // 如果不大,则证明此堆符合规则,则退出循环,
                break;
            }
        }
    }

以上是堆排序,我研究的时候想的不周全,以为每次重建堆,都会把数组中所有的数据重新排列建堆,所以想对堆排序进行一些改进,然后下面是我研究堆排序得到的一个算法,可惜效率不高,本质是选择排序。
看代码,我把它命名为堆选择排序

    public static void main(String[] args) {
        int[] arr = new int[10];  //构建一个空的一维数组
        for (int i = 0; i < arr.length; i++) {
            int temp = (int) (Math.random() * 1000) + 1;//随机产生一个 1~10 的整数
            arr[i] = temp;//将产生的数添加到数组
        }
        long a = System.currentTimeMillis();
        heapSort1(arr);
        long b = System.currentTimeMillis();
        System.out.println(a - b + "毫秒");
        for (int i = 0; i < arr.length; i++) {
            System.out.println(arr[i]);
        }
    }
    public static int[] heapSort1(int[] arr) {
    //取得数组中最大值
        heapChange(arr, 0);
        for (int j = arr.length - 1; j > 0; j--) {
        // 把最大值和末尾数据交换
            swap(arr, 0, j);
            // 从剩余数据中取得最大值
            heapChange(arr, arr.length - j);
        }
        return arr;
    }

    private static void heapChange(int[] array, int j) {
        int n = (array.length - j) / 2 - 1;
        // 这一步的意思是判断当前结点是否有左子节点,k=2*k+1的意思是如果左子结点下面还有左子节点,那么继续进行判断比较是否符合堆规则
        for (int k = 2 * n + 1; k < array.length - j && n >= 0; k = 2 * n + 1) {
            // 这一步的意思是判断是否有右子结点,并且右子结点数据比左子节点大
            if (k + 1 < array.length - j && array[k] < array[k + 1]) {
                k++;
            }
            // 取得左右子结点中较大的那一个和父节点比较,如果比父节点大,则交换
            if (array[k] > array[n]) {
                swap(array, n, k);
            }
                 // 比较下一个父结点
            n--;
        }
    }
        /**
     * 交换元素
     *
     * @param arr
     * @param a   元素的下标
     * @param b   元素的下标
     */
    public static void swap(int[] arr, int a, int b) {
        int temp = arr[a];
        arr[a] = arr[b];
        arr[b] = temp;
    }

我这个排序,是从数组中取得最大值,放到数组最后面,然后再从数组中取得剩余数据的最大值,放到最后面,本质是选择排序,效率没有堆排序高,原因是,堆排序一次建堆后,每次最多比较树高次就可以重新建堆,效率高,而我的,效率低。。
经此研究,发现堆排序效率还是可以的,但没有归并排序效率高好像

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
资源包主要包含以下内容: ASP项目源码:每个资源包中都包含完整的ASP项目源码,这些源码采用了经典的ASP技术开发,结构清晰、注释详细,帮助用户轻松理解整个项目的逻辑和实现方式。通过这些源码,用户可以学习到ASP的基本语法、服务器端脚本编写方法、数据库操作、用户权限管理等关键技术。 数据库设计文件:为了方便用户更好地理解系统的后台逻辑,每个项目中都附带了完整的数据库设计文件。这些文件通常包括数据库结构图、数据表设计文档,以及示例数据SQL脚本。用户可以通过这些文件快速搭建项目所需的数据库环境,并了解各个数据表之间的关系和作用。 详细的开发文档:每个资源包都附有详细的开发文档,文档内容包括项目背景介绍、功能模块说明、系统流程图、用户界面设计以及关键代码解析等。这些文档为用户提供了深入的学习材料,使得即便是从零开始的开发者也能逐步掌握项目开发的全过程。 项目演示与使用指南:为帮助用户更好地理解和使用这些ASP项目,每个资源包中都包含项目的演示文件和使用指南。演示文件通常以视频或图文形式展示项目的主要功能和操作流程,使用指南则详细说明了如何配置开发环境、部署项目以及常见问题的解决方法。 毕业设计参考:对于正在准备毕业设计的学生来说,这些资源包是绝佳的参考材料。每个项目不仅功能完善、结构清晰,还符合常见的毕业设计要求和标准。通过这些项目,学生可以学习到如何从零开始构建一个完整的Web系统,并积累丰富的项目经验。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值