堆排序-Python实现

简述

堆排序(Heap Sort)是一种基于比较的排序算法,它利用堆这种数据结构所设计的一种排序算法。堆排序是一种选择排序,它的最坏,最好,平均时间复杂度均为O(nlogn),它也是不稳定排序。

堆排序中的堆有大顶堆小顶堆两种。他们都是完全二叉树。

完全二叉树是指除了最后一层外,每一层都是完全填充的,并且最后一层的右边都是空的二叉树。大顶堆和小顶堆都是特殊的完全二叉树,它们的特点分别是每个节点的值都不小于(或不小于)其子节点的值,和每个节点的值都不大于(或不小于)其子节点的值。

将该堆按照排序放入列表

  • 大顶堆:所有的父节点的值都比孩子节点大,叶子节点值最小。root 根节点是第一个节点值最大公式表示:arr[i] >= arr[2i+1] && arr[i] >= arr[2i+2]
  • 小顶堆:和大顶堆相反,所有父节点值,都小于子节点值,root 根节点是 第一个节点值最小公式表示:arr[i] <= arr[2i+1] && arr[i] <= arr[2i+2]

ok,了解了这些定义。接下来,我们来看看堆排序的基本思想及基本步骤。

堆排序基本思想及步骤

堆排序的基本思想是:将待排序序列构造成一个大顶堆,此时,整个序列的最大值就是堆顶的根节点。将其与末尾元素进行交换,此时末尾就为最大值。然后将剩余n-1个元素重新构造成一个堆,这样会得到n个元素的次小值。如此反复执行,便能得到一个有序序列了。

在堆的结构中,堆中的最小值(最大值)总是位于堆的根结点。在堆排序中主要分为三步:
(1)创建大顶堆(BuildMaxHeap):将待排序元素列中的所有元素进行排列,生成一个大顶堆;
(2)将堆顶元素与末尾元素进行交换,将最大元素“沉”到元素列末端;
(3)调整顶堆,使其满足大顶堆定义;
(4)重复(2)(3)步骤,直至元素列有序。

以大顶堆为例

构造大顶堆

  • 如何调整
    我们从最后一个非叶子节点处开始调整。最后一个非叶子节点是最后一个叶子节点的父节点。
    若父节点的下标为 i ,那么左孩子节点下标为 i × 2 + 1,右孩子节点下标为 i × 2 + 2。
    设元素列的长度为 N,因为下标从0开始,所以最后一个叶子节点的下标是 N - 1。
    分两种情况:第一种是最后一个叶子节点是左孩子节点,那么 n - 1 = i × 2 + 1,即 i = n / 2 - 1
    第二种情况是最后一个叶子节点是右孩子节点(此时 n 是奇数)那么 n - 1 = i × 2 + 2,即 i = ( n - 1 ) / 2 - 1 = n / 2 - 1(向下取整)
    综上,最后一个非叶子节点的下标是 n / 2 - 1

  • 构造大根堆

    以升序排序序列 [20,50,20, 40, 70,10,80, 30,60]为例

    它对应的二叉树结构如下:

在我们的例子中,最后一个非叶子节点的下标是 9 / 2 - 1 = 3,因此调整顺序为:3–>2 -->1 -->0。

  1. 从最后一个父节点开始,将父节点、他所有的子节点中的最大值交换到父节点。父节点:3

  1. 将倒数第二个父节点同理交换,父节点:2

  1. 继续调整父节点:1

  1. 最后是根节点:0

  1. 注意很重要:务必注意-承接第3步。
    假设根节点值为:10, 当他和两个子节点70, 80。

父节点和两子节点中的大的(80)交换后位于父节点2:原来80的位置。

可是他还有子节点,且子节点中的值比根节点大,那就还需要以他为父节点构造一次,与子节点6 值为20交换一次。

同理在其他所有父节点的构造中都需要判断调整
忽略第五步。构造好的的大顶堆如下:

堆排序简略版

基本思路:将待排序序列构造成一个大顶堆,此时,整个序列的最大值就是堆顶的根节点。将其与末尾元素进行交换,此时末尾就为最大值。可称为有序区,然后将剩余n-1个元素重新构造成一个堆,估且称为堆区(未排序)。这样会得到n个元素的次小值。重复执行,有序区从:1—>n,堆区:n–>0,便能得到一个有序序列了。
每次将堆顶(根节点)最的的元素和堆尾列表最后一个元素交换,80 和40交换
即上面说的堆区(未排序):n–>0最大元素(根节点),和有序区从:1—>n,最后一个元素交换

按照上面原理继续排序,70, 30 交换。然后调整堆

堆顶元素60尾元素20交换后–>调整堆

最后结果

堆排序-python代码实现

现在排序这么一个序列:list_ = [4, 7, 0, 9, 1, 5, 3, 3, 2, 6]

    """
    堆排序  heap_sort
    
                         4
                       /   \
                     7      0
                   /  \    / \
                 9    1   5   3
               / \   /
             3   2  6
    
    list_ = [4, 7, 0, 9, 1, 5, 3, 3, 2, 6]
    """

代码实现一

    def swap(data, root, last):
        data[root], data[last] = data[last], data[root]
    
    #调整父节点 与孩子大小, 制作大顶堆
    def adjust_heap(data, par_node, high):
    
        new_par_node = par_node
        j = 2*par_node +1   #取根节点的左孩子, 如果只有一个孩子 high就是左孩子,如果有两个孩子 high 就是右孩子
    
        while j <= high: #如果 j = high 说明没有右孩子,high就是左孩子
            if j < high and data[j] < data[j+1]: #如果这儿不判断 j < high 可能超出索引
                # 一个根节点下,如果有两个孩子,将 j  指向值大的那个孩子
                j += 1
            if data[j] > data[new_par_node]: #如果子节点值大于父节点,就互相交换
                data[new_par_node], data[j] = data[j], data[new_par_node]
                new_par_node = j #将当前节点,作为父节点,查找他的子树
                j = j * 2 + 1
    
            else:
                # 因为调整是从上到下,所以下面的所有子树肯定是排序好了的,
                #如果调整的父节点依然比下面最大的子节点大,就直接打断循环,堆已经调整好了的
                break
    
    
    # 索引计算: 0 -->1 --->....
    #    父节点 i   左子节点:2i +1  右子节点:2i +2  注意:当用长度表示最后一个叶子节点时 记得 -1
    #    即 2i + 1 = length - 1 或者 2i + 2 = length - 1
    #    2i+1 + 1 = length 或 2i+2 + 1 = length
    #    2(i+1)=length 或 2(i+1)+1 = length
    #    设j = i+1  则左子节点(偶数):2j = length 和 右子节点(基数):2j+1 = length
    #    2j//2 = j == (2j+1)//2 这两个的整除是一样的,所以使用length//2 = j 然后 i + 1 = j
    #    i = j-1  = length//2 -1  #注意左子节点:2i+1 //2 =i  而右子节点:(2i+2)//2 = i+1 
    
    # 从第一个非叶子节点(即最后一个父节点)开始,即 list_.length//2 -1(len(list_)//2 - 1)
    
    # 开始循环到 root 索引为:0 的第一个根节点, 将所有的根-叶子 调整好,成为一个 大顶堆
    def heap_sort(lst):
        """
        根据列表长度,找到最后一个非叶子节点,开始循化到 root 根节点,制作 大顶堆
        :param lst: 将列表传入
        :return:
        """
        length = len(lst)
        last = length -1  #最后一个元素的 索引
        last_par_node = length//2 -1
    
        while last_par_node >= 0:
    
            adjust_heap(lst, last_par_node, length-1)
            last_par_node -= 1  #每调整好一个节点,从后往前移动一个节点
    
        # return lst
    
        while last > 0:
            #swap(lst, 0, last)
            lst[0], lst[last] = lst[last],lst[0]
            # 调整堆让 adjust 处理,最后已经排好序的数,就不处理了
            adjust_heap(lst, 0, last-1)
            last -= 1
    
        return lst #将列表返回
    
    
    
    if __name__ == '__main__':
        list_ = [4, 7, 0, 9, 1, 5, 3, 3, 2, 6]
        heap_sort(list_)
        print(list_)
    
    
    #最后结果为:
    [0, 1, 2, 3, 3, 4, 5, 6, 7, 9]

代码实现二

    import math
    
    def heap_sort(a):
        al = len(a)
    
        def heapify(a, i):
            left = 2 * i + 1
            right = 2 * i + 2
            largest = i
            if left < al and a[left] > a[largest]:
                largest = left
            if right < al and a[right] > a[largest]:
                largest = right
    
            if largest != i:
                a[i], a[largest] = a[largest], a[i]
                heapify(a, largest)
    
        # 建堆
        for i in range(math.floor(len(a) / 2), -1, -1):
            heapify(a, i)
    
        # 不断调整堆:根与最后一个元素
        for i in range(len(a) - 1, 0, -1):
            a[0], a[i] = a[i], a[0]
            al -= 1
            heapify(a, 0)
        return a
    
    
    if __name__ == '__main__':
        list_ = [4, 7, 0, 9, 1, 5, 3, 3, 2, 6]
        heap_sort(list_)
        print(list_)
    
    
    #最后结果为:
    [0, 1, 2, 3, 3, 4, 5, 6, 7, 9]

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

力扣精选算法100道——和为 K 的子数组[前缀和专题]

和为K的子数组链接 目录 第一步&#xff1a;了解题意​编辑 第二步&#xff1a;算法原理 第三步&#xff1a;代码 第一步&#xff1a;了解题意 数组中和为k的连续子数组&#xff0c;我们主要关注的是连续的&#xff0c; 比如[1,1,1],和为2的子数组有俩个&#xff0c;比如第…

搜索与图论(一)(深搜,广搜,树与图的存储遍历,拓扑排序)

一、DFS 往深里搜&#xff0c;搜到叶子结点那里&#xff0c;回溯&#xff0c;到可以继续到叶子结点深搜的位置。 1、回溯一定要恢复现场 2、定义一个与当前递归层数有关的终止条件&#xff08;题目要求的东西&#xff09; 3、每层都用循环判断是否存在可以dfs的路 输出数字…

flutter使用qr_code_scanner扫描二维码

qr_code_scanner仓库地址&#xff1a;qr_code_scanner | Flutter Package 需要添加android和ios的相机权限和本地相册权限&#xff1a; android中添加权限: 在android\app\build.gradle中修改&#xff1a;minSdkVersion 20 并且在android/app/src/main/AndroidManifest.xml中…

Kafka系列(一)【消息队列、Kafka的基本概念、Kafka的工作机制、Kafka可满足的需求、Kafka的特性、Kafka的应用场景】

kafka系列 一 一、消息队列1. 消息队列的来源2. 什么是消息队列3. 消息队列主要有哪些作用 二、Kafka的基本概念代理、生产者、消费者、消费者组主题、分区、副本、记录 三、了解 Kafka的工作机制-生产消息/消费消息四、Kafka可满足的需求五、Kafka的特性六、Kafka的场景 转自《…

漏洞挖掘 | 任意密码重置 + 存储型XSS

本文由掌控安全学院 - 老板来一份烧鹅饭 投稿 还是老样子&#xff0c;打开谷歌镜像&#xff0c;搜索site:edu.cn指定域名&#xff0c;搭配关键字登陆&#xff0c;注册&#xff0c;忘记密码&#xff0c;等等&#xff0c;或者xxx系统比较容易挖出通杀。 逻辑漏洞挖掘思路 1.登陆…

大带宽服务器托管的特点和考虑因素

很多公司和企业对于使用大带宽服务器的需求和存储不一样&#xff0c;为了满足不同的用户需求&#xff0c;大带宽服务器托管是个不错的选择&#xff0c;小编为您整理发布大带宽服务器托管的特点和要考虑的因素。 大带宽服务器托管是一种服务器托管服务&#xff0c;其主要特点是…

【数据分享】1929-2023年全球站点的逐年平均降水量(Shp\Excel\免费获取)

气象数据是在各项研究中都经常使用的数据&#xff0c;气象指标包括气温、风速、降水、湿度等指标&#xff0c;说到常用的降水数据&#xff0c;最详细的降水数据是具体到气象监测站点的降水数据&#xff01; 有关气象指标的监测站点数据&#xff0c;之前我们分享过1929-2023年全…

【图形学】投影和消隐简介

投影 正交投影 对于物体上任意一点的三维坐标P(x,y,z),投影后的三维坐标为 P ′ ( x ′ , y ′ , z ′ ) P^\prime(x^\prime,y^\prime,z^\prime) P′(x′,y′,z′),那么正交投影的方程为 { x ′ x y ′ y z ′ 0 \begin{cases} x^\primex\\y^\primey\\z^\prime0 \end{case…

2 月 7 日算法练习- 数据结构-并查集

并查集 并查集是一种图形数据结构&#xff0c;用于存储图中结点的连通关系。 每个结点有一个父亲&#xff0c;可以理解为“一只伸出去的手”&#xff0c;会指向另外一个点&#xff0c;初始时指向自己。 一个点的根节点是该点的父亲的父亲的的父亲&#xff0c;直到某个点的父亲…

【buuctf--来首歌吧】

用 Audacity 打开&#xff0c;左声道部分可以放大&#xff0c;可以按照长短转换成摩斯密码&#xff0c;放大后&#xff1a; ..... -... -.-. ----. ..--- ..... -.... ....- ----. -.-. -... ----- .---- ---.. ---.. ..-. ..... ..--- . -.... .---- --... -.. --... ----- -…

2024 年改变行业的人工智能主要趋势

1、导读 当我们迈入 2024 年时&#xff0c;了解人工智能趋势至关重要。它们不仅仅涉及技术进步&#xff1b;还涉及技术进步。它们意味着我们解决问题、做出决策和展望未来的方式发生了转变。本文旨在探索这些变革趋势&#xff0c;并强调人工智能如何不断突破可能性的界限&…

C++进阶(十二)lambda可变参数包装器

&#x1f4d8;北尘_&#xff1a;个人主页 &#x1f30e;个人专栏:《Linux操作系统》《经典算法试题 》《C》 《数据结构与算法》 ☀️走在路上&#xff0c;不忘来时的初心 文章目录 一、新的类功能1、默认成员函数2、类成员变量初始化3、 强制生成默认函数的关键字default:4、…

【软件设计师笔记】深入探究操作系统

【软件设计师笔记】计算机系统基础知识考点(传送门) &#x1f496; 【软件设计师笔记】程序语言设计考点(传送门) &#x1f496; &#x1f413; 操作系统的作用 1.通过资源管理提高计算机系统的效率 2.改善人机界面向用户提供友好的工作环境 &#x1f413; 操作系统的特征 …

leetcode(滑动窗口)483.找到字符中所有字母异位词(C++详细解释)DAY4

文章目录 1.题目示例提示 2.解答思路3.实现代码结果 4.总结 1.题目 给定两个字符串 s 和 p&#xff0c;找到 s 中所有 p 的 异位词 的子串&#xff0c;返回这些子串的起始索引。不考虑答案输出的顺序。 异位词 指由相同字母重排列形成的字符串&#xff08;包括相同的字符串&a…

03-抓包_封包_协议_APP_小程序_PC应用_WEB应用

抓包_封包_协议_APP_小程序_PC应用_WEB应用 一、参考工具二、演示案例&#xff1a;2.1、WEB应用站点操作数据抓包-浏览器审查查看元素网络监听2.2、APP&小程序&PC抓包HTTP/S数据-Charles&Fiddler&Burpsuite2.3、程序进程&网络接口&其他协议抓包-WireSh…

three.js 向量方向(归一化.normalize)

效果&#xff1a; <template><div><el-container><el-main><div class"box-card-left"><div id"threejs" style"border: 1px solid red"></div><div><p><el-button type"primary…

c#: 表达式树的简化

环境&#xff1a; .net 6 一、问题&#xff1f; 有下面的表达式&#xff1a; var nums new List<int> { 1, 2, 3 }; Expression<Func<int, bool>> exp i > i > nums.Max();我们知道&#xff0c;它其实就是&#xff1a;exp i > i > 3; 那么…

史上最全嵌入式(学习路线、应用开发、驱动开发、推荐书籍、软硬件基础)

废话不多说直接上思维导图&#xff01; 如果有觉得图片看不清楚的&#xff0c;有疑问的&#xff0c;可在评论区进行留言&#xff01; 群号&#xff1a; 228447240 嵌入式总括 嵌入式书籍推荐 嵌入式软件知识 嵌入式硬件知识 嵌入式应用开发 嵌入式驱动开发 嵌入式视频推荐: 韦…

5秒搭建PalWorld幻兽帕鲁游戏服务器,你信吗?

5秒搭建PalWorld幻兽帕鲁游戏服务器&#xff0c;你信吗&#xff1f;腾讯云推出幻兽帕鲁专属镜像系统&#xff0c;直接选择镜像&#xff0c;5秒搞定&#xff0c;全自动化部署。 幻兽帕鲁太火了&#xff0c;官方palworld服务器不稳定&#xff1f;不如自建服务器&#xff0c;基于…

双归同一运营商的 BGP 部署

一、拓朴如下&#xff1a; 要求&#xff1a; 1、AS100 只接收 AS200 和 300 的路由&#xff0c;不接收其它 AS 的明细路由&#xff1b; 2、对于 AS100 的业务流量出方向&#xff0c;所有到 AS200 和 300 的流量&#xff0c;优先选择 Line-1&#xff0c;而到 AS400 的流…