基于Python3的数据结构与算法 - 05 堆排序

目录

一、堆排序之树的基础知识

1. 树的定义

2. 树的一些概念

二、堆排序二叉树的基本知识

1. 二叉树的定义

2. 二叉树的存储方式(表达方式)

2.1 顺序存储方式

三、堆

1. 堆的定义

2. 堆的向下调整性质 

四、堆排序的过程 

1. 建造堆

五、时间复杂度

六、内置模块


一、堆排序之树的基础知识

1. 树的定义

  1. 树是一种数据结构  比如:目录结构
  2. 数是一种可以递归定义的数据结构
  3. 树是由n个节点组成的集合:
  • 如果n = 0,那这是一棵空树;
  • 如果n > 0,那存在1个节点作为树的根节点,其他节点可以分为m个集合,每个集合本身优势一棵树。

2. 树的一些概念

  1. 根节点、叶子节点:根节点如下图的A;叶子节点:不能分叉的节点为叶子节点(BCHIKLMNPQ)
  2. 树的深度(即结构共有几层,如下图树的结构有4层,A为第1层,P,Q为第4层)
  3. 树的度(即在树中,那个节点的分叉数最多,如下图中的A,树的度为6)
  4. 孩子节点/父节点(如E节点中,E是I的父节点;I是E的子节点)
  5. 子树(整个树中的一部分,例如EIJQO即为子树。)

二、堆排序二叉树的基本知识

1. 二叉树的定义

二叉树:度不超过2的树,即每个节点最多有两个孩子节点,且两个孩子节点被区分为左孩子节点和右孩子节点

满二叉树:一个二叉树,如果每一层的结点数都达到最大值,则这个二叉树就是满二叉树。

完全二叉树:叶节点只能出现在最下层和次下层,并且最下面一层的结点都集中在该层最左边的若干位置的二叉树。(从满二叉树最后一层拿走几个节点;即相对于满二叉树,最下面一层可以不满,但必须从左到右依次排过来

2. 二叉树的存储方式(表达方式)

  • 链式存储方式
  • 顺序存储方式(堆排序,用列表来存)

2.1 顺序存储方式

观察上图父节点 和孩子节点的编号下标的关系

1. 父节点与左孩子节点的编号下标的关系:

  •  0-1  1-3  2-5  3-7  4-9
  •  i → 2i+1

2. 父节点与右孩子节点的编号下标的关系:

  •  0-2  1-4  2-6  3-8  4-10
  • i → 2i+2

三、堆

1. 堆的定义

:一种特殊的完全二叉树结构。

大根堆:一棵完全二叉树,满足任一节点都比其孩子节点

小根堆:一棵完全二叉树,满足任一节点都比其孩子节点

2. 堆的向下调整性质 

向下调整性质:假设根节点的左右子树都是堆,但根节点 ,那么可以通过一次向下的调整来将其变成一个堆。

如下图所示:

通过将2往下移,9往上移动使得整体成为一个堆。

如下图所示:

四、堆排序的过程 

步骤:

  1. 建立堆
  2. 得到堆顶元素,为最大元素
  3. 去掉堆顶,将堆最后一个元素放到堆顶,此时可通过一次调整重新使堆有序
  4. 堆顶元素为第二最大元素
  5. 重复步骤3,直到堆变空

1. 建造堆

首先看最下面的元素是否满足条件,即看最后面的非叶子节点。

再接着调整上面的,先调整小的,再调整大的,最后调整整个(农村包围城市),当调整整个的时候,也就是除了堆顶元素其他下面的元素都有序,此时我们采用向下调整的性质。

例如下图中需要调整五次,从3开始,倒序到6. 

注意:不管左孩子还是右孩子,他们父节点的坐标都为(n-1)// 2

我们先写向下调整函数的代码:

def sift(li, low, high):  # 向下调整函数;初始条件应该为除了根节点,下面的子堆默认为有序的
    """
    :param li: 列表
    :param low: 根节点位置
    :param high: 根的最后一个元素的位置
    :return:
    """
    i = low  # i最开始指向根节点
    j = 2 * i + 1  # 左孩子
    tmp = li[low]  # 将堆顶元素存起来
    while j <= high:  # 只要j上面有数,没跑到结构外
        if j + 1 <= high and li[j + 1] > li[j]:  # 存在右孩子,且右孩子比左孩子大;不能交换
            j = j + 1  # j指向有孩子
        if li[j] > tmp:
            li[i] = li[j]  # 换数
            i = j  # 往下看一层
            j = 2 * i + 1
        else:  # 此时tmp更大,将tmp放回原位
            li[i] = tmp
            break  # 直接退出,因为sift默认下面就是堆的情况下
    else:  # 两种退出循环的条件;当其越界
        li[i] = tmp

当写完向下调整函数后,我们发现,如果我们需要对堆来排序,在对每个部分进行调整,最后调整整个部分(构建堆)。

def heap_sort(li):
    n = len(li)
    for i in range((n - 2)//2, -1, -1):  # i表示构建堆的时候调整的根的部分的下标
        sift(li, i, n - 1)  # 一直将整个堆的最后一个元素当作high,因为high作用在这里起比较的作用,判断是否越界。
    for i in range(n-1, -1, -1):       # i此时为最后一个元素
        li[0], li[i] = li[i], li[0]    # 将堆顶元素与最后一个元素换位置
        # 交换后i位置的元素前往堆顶,堆顶的元素放置一旁,i的元素空出来,此时最后一个元素为i-1
        sift(li, 0, i-1)           # 针对整个堆进行排

构建堆之后,再重复上述的2,3,4步骤。

最终即可对整个堆排完序。

示例的综合代码如下所示:

def sift(li, low, high):  # 向下调整函数;初始条件应该为除了根节点,下面的子堆默认为有序的
    """
    :param li: 列表
    :param low: 根节点位置
    :param high: 根的最后一个元素的位置
    :return:
    """
    i = low  # i最开始指向根节点
    j = 2 * i + 1  # 左孩子
    tmp = li[low]  # 将堆顶元素存起来
    while j <= high:  # 只要j上面有数,没跑到结构外
        if j + 1 <= high and li[j + 1] > li[j]:  # 存在右孩子,且右孩子比左孩子大;不能交换
            j = j + 1  # j指向有孩子
        if li[j] > tmp:
            li[i] = li[j]  # 换数
            i = j  # 往下看一层
            j = 2 * i + 1
        else:  # 此时tmp更大,将tmp放回原位
            li[i] = tmp
            break  # 直接退出,因为sift默认下面就是堆的情况下
    else:  # 两种退出循环的条件;当其越界
        li[i] = tmp


# 从孩子找父亲为(n-1)// 2;最后一个节点下标为n-1,则最后一个非叶子结点的下标为(n-2)//2
def heap_sort(li):
    n = len(li)
    for i in range((n - 2) // 2, -1, -1):  # i表示构建堆的时候调整的根的部分的下标
        sift(li, i, n - 1)  # 一直将整个堆的最后一个元素当作high,因为high作用在这里起比较的作用,判断是否越界。
        # 构建堆结束
    for i in range(n - 1, -1, -1):  # i此时为最后一个元素
        li[0], li[i] = li[i], li[0]  # 将堆顶元素与最后一个元素换位置
        # 交换后i位置的元素前往堆顶,堆顶的元素放置一旁,i的元素空出来,此时最后一个元素为i-1
        sift(li, 0, i - 1)  # 针对整个堆进行排序


li = [i for i in range(100)]
import random

random.shuffle(li)
print(li)
heap_sort(li)
print(li)

五、时间复杂度

首先对于我们的sift函数,其时间复杂度为O(logn).

def sift(li, low, high):  
    i = low  
    j = 2 * i + 1  
    tmp = li[low]  
    while j <= high:  
        if j + 1 <= high and li[j + 1] > li[j]:  
            j = j + 1  
        if li[j] > tmp:
            li[i] = li[j]  
            i = j  
            j = 2 * i + 1
        else:  
            li[i] = tmp
            break 
    else:  
        li[i] = tmp

sift最多走一个数的高度层,因此其复杂度相当于分半,为logn。

对于heap_sort,时间复杂度为(n/2 * logn)+(n-1)*logn = O(logn)(两个for循环)

六、内置模块

堆排序在Python中存在内置模块(heapq)(import heapq)(q为queue 优先队列)

常用函数:

  • heapofy(x)      #建堆
  • heappush(heap, item)    
  • heappop(heap)   #每次弹出一个最小元素

示例代码如下:

import heapq
import random
li = [i for i in range(100)]
random.shuffle(li)
print(li)
heapq.heapify(li)    #先建堆
n = len(li)
for i in range(n):
    print(heapq.heappop(li),end = ",")

输出结果如下:

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

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

相关文章

如何用好应用权限,保护隐私数据?银河麒麟桌面操作系统V10 SP1 2303 update2新功能解析

为您介绍银河麒麟桌面操作系统V10 SP1 2303 update2隐私设置和权限管理功能&#xff0c;为您的个人数据安全保驾护航。 说到个人数据隐私&#xff0c;在科技重塑生活本质的数字世界&#xff0c;个人信息遭受持续威胁。2018年&#xff0c;某国际知名社交平台因安全系统漏洞而遭…

CSS:弹性盒子Flexible Box布局

CSS:Flexible Box弹性盒子布局 一、flex布局原理 ​ flex是flexible Box的缩写,意为 ”弹性布局“&#xff0c;用来为盒状模型提供最大的灵活性&#xff0c;任何一个容器都可以指定为flex布局。 当我们的父盒子设置为flex布局之后&#xff0c;子元素的 float 、clear 和 vert…

抖音无水印视频关键词批量下载操作说明|视频批量采集工具

抖音无水印视频关键词批量下载工具是一款便捷实用的软件&#xff0c;通过关键词搜索功能可以轻松进行视频批量下载。QQ:290615413 以下是操作步骤及功能介绍&#xff1a; 打开软件后进入关键词搜索页面&#xff1a; 在软件中找到第一个选项卡&#xff0c;即为关键词搜索功能。 …

JOISC2022 复制粘贴(区间DP,字符串hash)

题目描述 题面 分析 这道题考场没有任何头绪&#xff0c;赛后也是看了许多题解才明白状态设计和转移的一步步思考过程。 首先我们需要想到 无论是屏幕上的字符串&#xff0c;还是剪切板上的字符串&#xff0c;在任何时候都必须是目标串的子串。这个非常好像&#xff0c;如果不…

视频汇聚/存储/压缩/诊断平台EasyCVR视频联网整合方案应用特点

随着科技的不断发展&#xff0c;监控视频在各个领域的应用越来越广泛。为了更好地管理和利用这些视频资源&#xff0c;视频联网与整合的需求也越来越多。通过视频联网技术将不同地理位置或不同设备的视频资源进行整合&#xff0c;实现实时共享和集中管理。视频联网整合方案的应…

快速创建百度百科,打造专属品牌词条

本文迅推客传媒将为小白详细讲解如何创建百度百科&#xff0c;并提供详细的教程。 创建百度百科账号 请先注册一个。注册完成后&#xff0c;登录百度账号。在搜索框中输入“百度百科”&#xff0c;进入百度百科官网。 选择创建词条类型 根据自己的需要选择相应的分类。例如&…

Outlook邮箱IMAP怎么开启?服务器怎么填?

Outlook邮箱IMAP服务器如何开启&#xff1f;Outlook设置IMAP的方法&#xff1f; Outlook邮箱作为其中的佼佼者&#xff0c;被广大用户所青睐。但在使用Outlook邮箱时&#xff0c;许多用户可能会碰到一个问题&#xff1a;如何开启IMAP服务&#xff1f;下面&#xff0c;蜂邮EDM就…

学习大数据,所必需的java基础(6)

文章目录 集合Set集合介绍HashSet集合的介绍和使用LinkedHashSet的介绍以及使用哈希值哈希值的计算方式HashSet的存储去重的过程 Map集合Map的介绍HashMap的介绍以及使用HashMap的两种遍历方式方式1&#xff1a;获取key&#xff0c;然后再根据key获取value方式2&#xff1a;同时…

trie树(前缀树)

前缀树 1. 前缀树的的介绍2.前缀树的实现2.1插入功能2.2删除功能2.3查找前缀和查找单词功能2.4 哈希表版本 1. 前缀树的的介绍 在计算机科学中&#xff0c;trie&#xff0c;又称前缀树或字典树&#xff0c;是一种有序树&#xff0c;用于保存关联数组&#xff0c;其中的键通常是…

《Spring Security 简易速速上手小册》第1章 Spring Security 概述(2024 最新版)

文章目录 1.1 Spring Security 的重要性1.1.1 基础知识详解1.1.2 主要案例&#xff1a;用户认证与授权1.1.3 拓展案例 1&#xff1a;OAuth2 社交登录1.1.4 拓展案例 2&#xff1a;JWT 认证 1.2 Spring Security 的核心特性1.2.1 基础知识详解1.2.2 主要案例&#xff1a;基于角色…

11.网络游戏逆向分析与漏洞攻防-游戏网络架构逆向分析-接管游戏接收网络数据包的操作

内容参考于&#xff1a;易道云信息技术研究院VIP课 上一个内容&#xff1a;接管游戏发送数据的操作 码云地址&#xff08;master 分支&#xff09;&#xff1a;https://gitee.com/dye_your_fingers/titan 码云版本号&#xff1a;8256eb53e8c16281bc1a29cb8d26d352bb5bbf4c 代…

Android Duplicate class 排除重复类

一、起因&#xff1a; 在迭代开发的时候&#xff0c;发现2个ijk很多类重复。但又2个库实现的功能是不一样&#xff0c;目前不能合并。但又想保留2个功能。需要排除其中一个库。 二、报错如何下图&#xff1a; 三、解决方法&#xff1a; 3.1 在terminal 也就是命令行处输入 …

js 面试 什么是WebSockets?HTTP和HTTPS有什么不同?web worker是什么?

概念&#xff1a; webSocket 是一种在客户端和服务端之间建立持久连接的协议&#xff0c;它提供全双工通信通道&#xff0c;是服务器可以主动向客户端推送数据&#xff0c;同时也可以接受客户端发送的数据。 1 webSocket与https区别&#xff1f; 在网络通信中&#xff0c;We…

【mysql版本修改】

1、使用telnet确认当前mysql版本号 telnet <MySQL服务器IP地址> <MySQL端口号> telnet 192.168.38.20 33062、使用strings查看/usr/sbin/mysqld中包含版本号的字符串 # 查看/usr/sbin/mysqld文件中是否包含对应的版本号 strings /usr/sbin/mysqld | grep 5.7.30 …

Unity | 动态读取C#程序集实现热更新

目录 一、动态语言 二、创建C#dll 1.VS中创建一个C#语言的库工程 2.添加UnityEngine.dll的依赖 3.编写代码&#xff0c;生成dll 三、Unity使用dll 一、动态语言 计算机编程语言可以根据它们如何将源代码转换为可以执行的代码来分类为静态语言和动态语言。 静态语言&…

Spark Bloom Filter Join

1 综述 1.1 目的 Bloom Filter Join&#xff0c;或者说Row-level Runtime Filtering&#xff08;还额外有一条Semi-Join分支&#xff09;&#xff0c;是Spark 3.3对运行时过滤的一个最新补充   之前运行时过滤主要有两个&#xff1a;动态分区裁剪DPP&#xff08;开源实现&am…

ISO_IEC_18598-2016自动化基础设施管理(AIM)系统国际标准解读(一)

██ ISO_IEC_18598-2016是什么标准&#xff1f; ISO/IEC 18598国际标准是由ISO&#xff08;国际标准化组织&#xff09;/IEC&#xff08;国际电工委员会&#xff09;联合技术委员会1-信息技术的第25分委员会-信息技术设备互连小组制定的关于信息基础设施自动化管理的国际标准&…

C++中atomic的使用

atomic使用 概述介绍使用场景头文件atomic的使用创建load()store()exchange()compare_exchange_weakcompare_exchange_strong&#xff08;&#xff09;fetch_add()fetch_sub()fetch_and()fetch_or()fetch_xor() 示例实现代码运行结果 概述 本文只要讲述C11中atomic的使用&…

一文读懂Prometheus和Grafana的区别(适合小白)

Prometheus和Grafana是两种开源软件&#xff0c;分别用于监控和可视化数据。它们的主要功能和特点如下&#xff1a; Prometheus 监控系统&#xff1a;Prometheus是一个专门用于收集和存储时间序列数据的监控系统。它可以从各种目标&#xff08;如服务器、数据库等&#xff09…

Spring中的事务和事务的传播机制

事务是一组操作的集合&#xff0c;不可以被分割。事务会把所有的操作作为一个整体&#xff0c;这组操作要么全部成功&#xff0c;要么全部失败。 事务有三种操作&#xff1a; 开启事务&#xff1b;提交事务&#xff1b;回滚事务。 如果代码的执行逻辑是这样&#xff1a; 开…