Day_48堆排序

news2024/11/26 23:40:23

目录

一. 关于堆排序

        1. 堆的定义

二. 堆排序的实现

        1. 堆排序的思路

        2. 堆排序的问题分析

        3. 堆排序的具体实施

        4. 效率分析

三. 堆排序的代码实现

        1. 堆排序

        2. 调整堆(核心代码)

四. 代码展示

五. 数据测试

六. 总结


一. 关于堆排序

        1. 堆的定义

        n个关键字序列L[1...n]称为堆,当且仅当该序列满足:

        ①L(i)≥L(2i)且L(i)≥L(2i+1)或

        ②L(i)≤L(2i)且L(i)≤L(2i+1)————————(1≤i≤n/2)

        首先堆是一个完全二叉树(这是我们后面进行堆排序的前提条件),满足条件①的堆称为大根堆,大根堆的最大元素存放在根节点,且其任意一个非根节点的值小于或等于其双亲节点值。满足条件②的堆称为小根堆,小根堆的最小元素存放在根节点,且其任意一个非根节点的值大于或等于其双亲节点值,小根堆的定义恰好相反,根节点是最小元素。如下图所示大根堆,小根堆。

小根堆
大根堆

 

二. 堆排序的实现

        1. 堆排序的思路

        使用堆的元素下沉思想,即我先根据给定的数组序列构造一个堆,我每一次取根节点,并且将根节点删除(插入到最后一个位置),再对去掉根节点的数字序列构造一个堆;重复上述步骤即可得到最终的排序结果,除此之外这里没有递归调用,且使用的空间是常数,故在给定空间进行原地排序。具体步骤如下:

        a、给定任意待排序的数组可以看作是是一颗完全二叉树(顺序存储完全二叉树的性质)。
        b、然后将此二叉树转换为一个大顶堆。
        c、最后依次将大顶堆的最大元素放在指定位置(去掉最大元素后,堆最后一个元素坐标的下一个坐标)。

        2. 堆排序的问题分析

        上面的思路写的其实很简单,a和c都很好理解,

        现在我们的问题是:①最关键的b部,我们应该怎么样从无到有根据数字序列建立一个堆呢?②我们首次建立完成堆,并且再删除了根节点元素之后就破坏了原来的堆,我们又该如何重新建立堆呢?接下来我将叙述具体的实施过程。

        3. 堆排序的具体实施

        首先对于第一个问题:堆排序怎么样构建一个初始(大根)堆?

        n个节点的完全二叉树,最后一个节点是第n/2个节点的孩子(完全二叉树的性质)。那么我们就从这个节点开始,对第n/2个节点为根的子树筛选(对于大根堆,若子树根节点的关键字小于左右还在中的关键字较大者,则交换根节点和左右孩子中的较大者),使以n/2节点为根的子树成为一个大根堆。接着我们继续向前一次对n/2-1——1为根的子树进行建堆,看每一个根节点是否都大于左右孩子中的较大者,若大于则不操作,若不大于则交换根节点和左右孩子中的较大者,并且注意,这里交换之后由于是在完全二叉树的上层,很有可能会破坏下层我们已经建立好的子树的大根堆,所以,我们还要对这个根节点进行筛查,若这个根节点仍然小于调整后的左右孩子节点的值,那么我们继续调整这个根节点,直到他满足大根堆的定义为止。最后我们反复利用上面的调整关系,使n/2号节点——1号节点的每一个节点都满足大根堆条件,故此建立初始堆完成。

        接着我们考虑第二个问题:删除了根节点元素之后就破坏了原来的堆,我们又该如何重新建立(大根)堆?

        其实对于这个问题,我们上面已经有了解答,由于是将排好序的大根堆的根节点和最后一个节点互换位置,那么我们可以得到只有根节点一个节点破坏了大根堆,那么我们只需调整根节点的位置即可,不需要再从n-1号节点一直筛选到1号节点了。根节点和它的第一个左右孩子节点比较,是否大于等于左右孩子中的较大者;若不满足则将根节点和左右孩子中的最大者交换位置,再判断这个时候根节点的位置是否满足大根堆,即是否大于等于左右孩子中的较大者;若大于等于则调整结束;若不满足则继续调整这个根节点的位置直到满足条件为止。接着我们又得到了一个排好序的大根堆,继续讲根节点和倒数第二个节点互换位置继续上层循环,直到序列只有一个节点为止。

        为了方便理解,下图是堆排序(大根堆)的过程:

初始i=4(n/2)
i=3(n/2-1)

 

 

i=2(n/2-2)
i=1(n/2-3)

 

破坏下层大根堆,继续i=1
最终结果

 

        4. 效率分析

        空间效率:仅使用了常数个辅助单元,空间复杂度为O(1)。

        时间效率:建堆时间O(n),之后又n-1次向下调整操作,每次调整的时间复杂度为O(h),h=log_{2}n,故在最好,最坏和平均情况下,堆排序的时间复杂度为O(nlog_{2}n)

        稳定性:进行筛选时,有可能把后面相同 关键字的元素调整到前面,故是一种不稳定的算法。

三. 堆排序的代码实现

        1. 堆排序

        堆排序的总函数,非核心代码,相当于一个框架。

        step1:给定调整大根堆函数;

        step2:交换根节点和最后一个节点之后,破坏了大根堆,我们只需调整交换之后的根节点,使数字序列再次变为大根堆,继续step2的循环,直到最后一个节点结束。

    /**
     *********************
     * Heap sort. Maybe the most difficult sorting algorithm.
     *********************
     */
    public void heapSort() {
        DataNode tempNode;
        // Step 1. Construct the initial heap.
        for (int i = length / 2 - 1; i >= 0; i--) {
            adjustHeap(i, length);
        } // Of for i
        System.out.println("The initial heap: " + this + "\r\n");

        // Step 2. Swap and reconstruct.
        for (int i = length - 1; i > 0; i--) {
            tempNode = data[0];
            data[0] = data[i];
            data[i] = tempNode;

            adjustHeap(0, i);
            System.out.println("Round " + (length - i) + ": " + this);
        } // Of for i
    }// Of heapSort

        2. 调整堆(核心代码)

        这部分代码就是堆排序的核心,函数名adjustHeap,传入两个参数,第一个参数paraStart是从哪个节点开始调整完全二叉树的子树为大根堆的节点位置,第二个参数paraLength是控制完全二叉树跳调整的范围(例如我再交换完根节点和最后一个节点之后,paraLength需要减1(因为最后一个节点相当于被删除))。

tempNode变量记录从哪个节点开始调整完全二叉树的子树为大根堆的节点数据

tempParent变量记录从哪个节点开始调整完全二叉树的子树为大根堆的节点位置

tempKey变量记录从哪个节点开始调整完全二叉树的子树为大根堆的节点标签

tempChild作为paraStart节点的左右孩子啊中标签最大的那个孩子节点的位置信息。接着进行判断,若父母节点的标签tempKey < 孩子节点的标签data[tempChild].key(不满足大根堆条件),则交换父母节点和孩子节点中的较大值;同时进行判断交换完之后的tempParent节点是否在新的位置满足大根堆,若满足结束循环,这个节点调整完完毕;若不满足,继续上述操作直到完成为止。最后我们得到了节点paraStart的最终位置信息。

        综上所述其实问题的关键我觉得是理解paraStart和paraLength参数是表示什么,adjustHeap函数是将paraStart节点值一直向下调整到满足大根堆为止(其他节点一只耳没动(除了paraStart的孩子节点,因为可能要互换位置,其他子树一直没有发生位置变化)),所以这里我们只调整节点paraStart的位置信息,paraLength控制数字序列的长度。

    /**
     *********************
     * Adjust the heap.
     *
     * @param paraStart  The start of the index.
     * @param paraLength The length of the adjusted sequence.
     *********************
     */
    public void adjustHeap(int paraStart, int paraLength) {
        DataNode tempNode = data[paraStart];
        int tempParent = paraStart;
        int tempKey = data[paraStart].key;

        for (int tempChild = paraStart * 2 + 1; tempChild < paraLength; tempChild = tempChild * 2 + 1) {
            // The right child is bigger.
            if (tempChild + 1 < paraLength) {
                if (data[tempChild].key < data[tempChild + 1].key) {
                    tempChild++;
                } // Of if
            } // Of if

            System.out.println("The parent position is " + tempParent + " and the child is " + tempChild);
            if (tempKey < data[tempChild].key) {
                // The child is bigger.
                data[tempParent] = data[tempChild];
                System.out.println("Move " + data[tempChild].key + " to position " + tempParent);
                tempParent = tempChild;
            } else {
                break;
            } // Of if
        } // Of for tempChild

        data[tempParent] = tempNode;

        System.out.println("Adjust " + paraStart + " to " + paraLength + ": " + this);
    }// Of adjustHeap

四. 代码展示

        主类:

package Day_48;

import Day_41.DataArray;

public class demo1 {
    /**
     *********************
     * The entrance of the program.
     *
     * @param args Not used now.
     *********************
     */
    public static void main(String args[]) {
//        System.out.println("\r\n-------sequentialSearchTest-------");
        int []paraKeyArray;
        paraKeyArray=new int[]{11,2,3};
        String[] paraContentArray = new String[]{"121","21","324"};
//        System.out.println(paraKeyArray.length);
        DataArray test=new DataArray(paraKeyArray,paraContentArray);

//        test.insertionSort();
//        System.out.println("Result\r\n" + test);
        test.heapSortTest();


    }// Of main
}

        调用类(这个类太长了,我只保留了这一节的代码)



    /**
     *********************
     * Heap sort. Maybe the most difficult sorting algorithm.
     *********************
     */
    public void heapSort() {
        DataNode tempNode;
        // Step 1. Construct the initial heap.
        for (int i = length / 2 - 1; i >= 0; i--) {
            adjustHeap(i, length);
        } // Of for i
        System.out.println("The initial heap: " + this + "\r\n");

        // Step 2. Swap and reconstruct.
        for (int i = length - 1; i > 0; i--) {
            tempNode = data[0];
            data[0] = data[i];
            data[i] = tempNode;

            adjustHeap(0, i);
            System.out.println("Round " + (length - i) + ": " + this);
        } // Of for i
    }// Of heapSort

    /**
     *********************
     * Adjust the heap.
     *
     * @param paraStart  The start of the index.
     * @param paraLength The length of the adjusted sequence.
     *********************
     */
    public void adjustHeap(int paraStart, int paraLength) {
        DataNode tempNode = data[paraStart];
        int tempParent = paraStart;
        int tempKey = data[paraStart].key;

        for (int tempChild = paraStart * 2 + 1; tempChild < paraLength; tempChild = tempChild * 2 + 1) {
            // The right child is bigger.
            if (tempChild + 1 < paraLength) {
                if (data[tempChild].key < data[tempChild + 1].key) {
                    tempChild++;
                } // Of if
            } // Of if

            System.out.println("The parent position is " + tempParent + " and the child is " + tempChild);
            if (tempKey < data[tempChild].key) {
                // The child is bigger.
                data[tempParent] = data[tempChild];
                System.out.println("Move " + data[tempChild].key + " to position " + tempParent);
                tempParent = tempChild;
            } else {
                break;
            } // Of if
        } // Of for tempChild

        data[tempParent] = tempNode;

        System.out.println("Adjust " + paraStart + " to " + paraLength + ": " + this);
    }// Of adjustHeap

    /**
     *********************
     * Test the method.
     *********************
     */
    public static void heapSortTest() {
        int[] tempUnsortedKeys = { 5, 3, 6, 10, 7, 1, 9 };
        String[] tempContents = { "if", "then", "else", "switch", "case", "for", "while" };
        DataArray tempDataArray = new DataArray(tempUnsortedKeys, tempContents);

        System.out.println(tempDataArray);

        tempDataArray.heapSort();
        System.out.println("Result\r\n" + tempDataArray);
    }// Of heapSortTest

五. 数据测试

        运行数据

六. 总结

        这一节的堆排序可以算的上是数据结构排序算法里面比较难的部分了,这里有三个问题是关键①如何构建初始(大根)堆,②由于交换排好序的根节点和最后一个节点,破坏了(大根)堆,我们该如何修复他?③再构建大根堆的过程中(由于是从下往上构建大根堆)在上层调整节点位置时,难免会破坏下层的大根堆子树,我们又该做什么样的解决办法。

        其实对于这个算法我更想形象的把这个算法比作一个系统,每次根据这个系统的运行结果调整参数(节点值),然后不断迭代,最终得到我们想要的答案。

        

        

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/629428.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Shell脚本学习记录

shell教程 第一个shell脚本 打开文本编辑器(可以使用 vi/vim 命令来创建文件)&#xff0c;新建一个文件 test.sh&#xff0c;扩展名为 sh&#xff08;sh代表shell&#xff09;。 #!/bin/bash echo "Hello World !" #! 是一个约定的标记&#xff0c;它告诉系统这个…

ieda codeformatV2.xml

ieda codeformatV2.xml 目录概述需求&#xff1a; 设计思路实现思路分析1.codeformatV22.codeformatV23.codeformatV24.codeformatV25.数据处理器 拓展实现 参考资料和推荐阅读 Survive by day and develop by night. talk for import biz , show your perfect code,full busy&…

MySQL数据库基础 08

第八章 聚合函数 1. 聚合函数介绍1.1 AVG和SUM函数1.2 MIN和MAX函数1.3 COUNT函数 2. GROUP BY2.1 基本使用2.2 使用多个列分组2.3 GROUP BY中使用WITH ROLLUP 3. HAVING3.1 基本使用3.2 WHERE和HAVING的对比 4. SELECT的执行过程4.1 查询的结构4.2 SELECT执行顺序4.3 SQL 的执…

MT1619

MT1619 是一款PD快充开关电源转换器芯片&#xff0c;其内部集成了一颗高集成度、高性能的电流模式 PWM 控制器和一颗功率 MOSFET。它适用于小于 30W 的开关电源设备。MT1619 具有恒功率功能&#xff0c;特别适用于 PD 充电器、电源适配器等中小功率的开关电源设备。极低的启动电…

[golang 微服务] 5. 微服务服务发现介绍,安装以及consul的使用,Consul集群

一.服务发现介绍 引入 上一节讲解了使用 gRPC创建微服务,客户端的一个接口可能需要调用 N个服务,而不同服务可能存在 不同的服务器,这时&#xff0c;客户端就必须知道所有服务的 网络位置&#xff08;ipport&#xff09;&#xff0c;来进行连接服务器操作,如下图所示: 以往的做…

赋值运算符重载实例:Date类(结尾附源码)

赋值运算符重载实例&#xff1a;Date类 文章目录 赋值运算符重载实例&#xff1a;Date类一、构造日期类二、获取某年某月天数以及检查合法1.获取某年某月天数2.检查日期合法3.打印日期类 三、不同运算符的重载&#xff08;1&#xff09; ; !&#xff08;2&#xff09;> ; &g…

Lecture 13 Formal Language Theory Finite State Automata

目录 什么是语言&#xff1f;Formal Language Theory 形式语言理论动机例子除了从属问题之外的问题Regular Languages 正则语言Finite State Acceptor 正则语言的性质Derivational MorphologyWeighted FSAFinite State Transducer (FST)FST for Inflectional MorphologyNon-Reg…

Java Web实现用户登录功能

文章目录 一、纯JSP方式实现用户登录功能&#xff08;一&#xff09;实现思路1、创建Web项目2、创建登录页面3、创建登录处理页面4、创建登录成功页面5、创建登录失败页面6、编辑项目首页 &#xff08;三&#xff09;测试结果 二、JSPServlet方式实现用户登录功能&#xff08;一…

软考A计划-系统架构师-官方考试指定教程-(11/15)

点击跳转专栏>Unity3D特效百例点击跳转专栏>案例项目实战源码点击跳转专栏>游戏脚本-辅助自动化点击跳转专栏>Android控件全解手册点击跳转专栏>Scratch编程案例 &#x1f449;关于作者 专注于Android/Unity和各种游戏开发技巧&#xff0c;以及各种资源分享&am…

第四十周周报

学习目标&#xff1a; 一、Stable Diffusion 论文 学习时间&#xff1a; 2023.6.3-2023.6.9 学习产出&#xff1a; 一、模型进展 相比前两周的结果&#xff0c;本周改进了一下参数&#xff0c;FID达到了9 前两周结果为10.258 本周相比前两周FID降低1 二、High-Resolu…

数据结构之树与二叉树——算法与数据结构入门笔记(五)

本文是算法与数据结构的学习笔记第五篇&#xff0c;将持续更新&#xff0c;欢迎小伙伴们阅读学习。有不懂的或错误的地方&#xff0c;欢迎交流 引言 前面章节介绍的都是线性存储的数据结构&#xff0c;包括数组、链表、栈、队列。本节带大家学习一种非线性存储的数据结构&…

【MySQL】- 05 sql 语句练习题

sql 语句练习题 一 测试数据的准备二、数据查询1、查询"01"课程比"02"课程成绩高的学生的信息及课程分数2、查询"01"课程比"02"课程成绩低的学生的信息及课程分数3、查询平均成绩大于等于60分的同学的学生编号和学生姓名和平均成绩4、…

MySQL数据库基础 06

第六章 多表查询 1. 一个案例引发的多表连接1.1 案例说明1.2 笛卡尔积&#xff08;或交叉连接&#xff09;的理解1.3 案例分析与问题解决 2. 多表查询分类讲解分类1&#xff1a;等值连接 vs 非等值连接等值连接非等值连接 分类2&#xff1a;自连接 vs 非自连接分类3&#xff1a…

动态网站开发02:Java Web概述

文章目录 一、 XML基础&#xff08;一&#xff09;XML概述1、XML2、XML与HTML的比较 &#xff08;二&#xff09;XML语法1、XML文档的声明2、XML元素的定义3、XML属性的定义4、XML注释的定义5、XML文件示例 &#xff08;三&#xff09;DTD约束1、什么是XML约束2、什么是DTD约束…

MM32F3273G8P火龙果开发板MindSDK开发教程5 - Gcc编译环境的配置

MM32F3273G8P火龙果开发板MindSDK开发教程5 - Gcc编译环境的配置 1、准备工作 用了几天Keil后&#xff0c;实在用不习惯&#xff0c;只好回到macos系统中来搭建gcc的编译环境。但是百问网火龙果开发板自带的DAP-Link在pyocd中根本识别不到&#xff0c;所以烧录还需要重新购置…

MySQL数据库基础 07

第七章 单行函数 1. 函数的理解1.1 什么是函数1.2 不同DBMS函数的差异1.3 MySQL的内置函数及分类 2. 数值函数2.1 基本函数2.2 角度与弧度互换函数2.3 三角函数2.4 指数与对数2.5 进制间的转换 3. 字符串函数4. 日期和时间函数4.1 获取日期、时间 4.2 日期与时间戳的转换 4.3 获…

一个关于宏定义的问题,我和ChatGPT、NewBing、Google Bard、文心一言 居然全军覆没?

文章目录 一、问题重述二、AI 解题2.1 ChatGPT2.2 NewBing2.3 Google Bard2.4 文心一言2.5 小结 一、问题重述 今天在问答模块回答了一道问题&#xff0c;要睡觉的时候&#xff0c;又去看了一眼&#xff0c;发现回答错了。 问题描述&#xff1a;下面的z的值是多少。 #define…

【Redis】Redis持久化机制RDB与AOF

目录 一、RDB 1、概念 2、RDB文件保存 3、执行RDB 4、触发RDB 5、fork原理 6、RDB的缺点 二、AOF 1、概念 2、开启AOF 3、触发AOF 4、触发重写AOF 三、区别 一、RDB 1、概念 RDB全称为Redis Database Backup File&#xff08;Redis数据备份文件&#xff09;&…

提示工程师指南3-Prompt工程-高级提示

高阶Prompting 到这一步&#xff0c;应该很明显&#xff0c;改进提示有助于在不同任务上获得更好的结果。这就是Prompt工程背后的整个理念。 虽然之前的例子很有趣&#xff0c;但在我们深入了解更高级的概念之前&#xff0c;让我们先正式地介绍一些概念。 文章目录 高阶Promp…

中国机器元宇宙手术机器人市场迎来爆发期,思哲睿能否借势上市?

手术机器人作为一种能够辅助医生进行精准、微创的外科手术的高端医疗设备&#xff0c;近年来受到了国内外医疗界的广泛关注和重视。随着我国人口老龄化、医疗需求增加、医疗技术进步等因素的推动&#xff0c;手术机器人市场规模呈现快速增长态势。 在这样一个充满机遇和挑战的市…