基于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 + 1else:  # 此时tmp更大,将tmp放回原位li[i] = tmpbreak  # 直接退出,因为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-1sift(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 + 1else:  # 此时tmp更大,将tmp放回原位li[i] = tmpbreak  # 直接退出,因为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-1sift(li, 0, i - 1)  # 针对整个堆进行排序li = [i for i in range(100)]
import randomrandom.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 + 1else:  li[i] = tmpbreak 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 = ",")

输出结果如下:

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

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

相关文章

SpringCloud认识微服务

文章目录 1.1.单体架构1.2.分布式架构1.3.微服务1.4.SpringCloud1.5.总结 随着互联网行业的发展&#xff0c;对服务的要求也越来越高&#xff0c;服务架构也从单体架构逐渐演变为现在流行的微服务架构。这些架构之间有怎样的差别呢&#xff1f; 微服务架构是一种架构模式&…

软考51-上午题-【数据库】-索引

一、索引的定义 在数据库中&#xff0c;索引使得数据库程序无需对整个表进行扫描&#xff0c;就可以在其中找到所需数据。数据库中的索引是某个表中一列或者若干列&#xff0c;值的集合和相应的指向表中物理标识这些值的数据页逻辑指针清单。 二、索引的创建和删除 2-1、索引…

ThreeJS 几何体顶点position、法向量normal及uv坐标

文章目录 几何体的顶点position、法向量normal及uv坐标UV映射UV坐标系UV坐标与顶点坐标设置UV坐标案例1&#xff1a;使用PlaneGeometry创建平面缓存几何体案例2&#xff1a;使用BufferGeometry创建平面缓存几何体 法向量 - 顶点法向量光照计算案例1&#xff1a;不设置顶点法向量…

Linux shell:补充命令的使用

目录 一.导读 二.正文 三.结语 一.导读 上一篇介绍了脚本的简单概念以及使用&#xff0c;现在补充一些命令。 二.正文 目前处于全局目录&#xff0c;通过mkdir创建名我为day01的文件。 通过cd命令day01 切换至day01文件当中。 使用vim文本编辑器文件名&#xff08;firstdir&…

【algorithm】算法基础课---排序算法(附笔记 | 建议收藏)

&#x1f680;write in front&#x1f680; &#x1f4dd;个人主页&#xff1a;认真写博客的夏目浅石. &#x1f381;欢迎各位→点赞&#x1f44d; 收藏⭐️ 留言&#x1f4dd; &#x1f4e3;系列专栏&#xff1a;AcWing算法学习笔记 &#x1f4ac;总结&#xff1a;希望你看完…

day 45 ● 70. 爬楼梯 (进阶)● 322. 零钱兑换 ● 279.完全平方数

#include<bits/stdc.h> using namespace std; int main(){int n,m;cin>>n>>m;vector<int> dp(33,0);dp[0]1;for(int i0;i<n;i){for(int j1;j<m;j){if(i>j)dp[i]dp[i-j];}}// return dp[n];cout<<dp[n]<<endl;} 当然注意 力扣是 …

2.23作业

1.自己实现单向循环链表的功能 //loop_list.c#include"loop_list.h" //创建单向循环链表 loop_p create_head() {loop_p H(loop_p)malloc(sizeof(loop_list));if(HNULL){printf("空间申请失败\n");return NULL;}H->len0;H->nextH;return H; }//创建…

C语言中如何进行内存管理

主页&#xff1a;17_Kevin-CSDN博客 收录专栏&#xff1a;《C语言》 C语言是一种强大而灵活的编程语言&#xff0c;但与其他高级语言不同&#xff0c;它要求程序员自己负责内存的管理。正确的内存管理对于程序的性能和稳定性至关重要。 一、引言 C 语言是一门广泛使用的编程语…

学习Android的第十八天

目录 Android 可复用 BaseAdapter 为什么使用BaseAdapter&#xff1f; 如何使用BaseAdapter&#xff1f; Android GridView 网格视图 GridView 属性 示例 Android Spinner 下拉选项框 Spinner Spinner 属性 示例 Android AutoCompleteTextView 自动完成文本框 Auto…

饲料加工设备让饲料厂和养殖场生产轻松高效

亲爱的饲料厂和养殖场朋友们&#xff0c;你们是不是正在寻找一款方便高效的饲料加工设备呢&#xff1f;那么我们的产品就是你们选择&#xff01; 郑州永兴专为饲料生产而设计的饲料加工设备&#xff0c;无论是养殖场还是饲料厂&#xff0c;都离不开它的帮助。我们提供大中小型…

51单片机-(定时/计数器)

51单片机-&#xff08;定时/计数器&#xff09; 了解CPU时序、特殊功能寄存器和定时/计数器工作原理&#xff0c;以定时器0实现每次间隔一秒亮灯一秒的实验为例理解定时/计数器的编程实现。 1.CPU时序 1.1.四个周期 振荡周期&#xff1a;为单片机提供定时信号的振荡源的周期…

卡尔曼滤波器的定义,实例和代码实现

卡尔曼滤波器(Kalman filter)是一种高效的递归滤波器, 能够从一系列包含噪音的测量值中估计动态系统的状态. 因为不需要存储历史状态, 没有复杂计算, 非常适合在资源有限的嵌入式系统中使用. 常用于飞行器的导引, 导航及控制, 机械和金融中的时间序列分析, 轨迹最佳化等. 卡尔曼…

LeetCode59. 螺旋矩阵 II(C++)

LeetCode59. 螺旋矩阵 II 题目链接代码 题目链接 https://leetcode.cn/problems/spiral-matrix-ii/ 代码 class Solution { public:vector<vector<int>> generateMatrix(int n) {vector<vector<int>> res(n, vector<int>(n, 0));int startx …

Linux第67步_linux字符设备驱动_注册和注销

1、字符设备注册与注销的函数原型” /*字符设备注册的函数原型*/ static inline int register_chrdev(unsigned int major,\ const char *name, \ const struct file_operations *fops) /* major:主设备号&#xff0c;Limnux下每个设备都有一个设备号&#xff0c;设备号分…

点云检测网络PointPillar

1. 提出PointPillar的目的 在此之前对于不规则的稀疏的点云的做法普遍分为两派: 一是把点云数据量化到一个个Voxel里&#xff0c;常见的有VoxelNet和SECOND , 但是这种做法比较普遍的问题是由于voxel大部分是空集所以会浪费算力(SECOND利用稀疏卷积解决了它) &#xff0c;但是…

MNIST数据集下载(自动下载)

&#x1f4da;**MNIST数据集下载(自动下载)**&#x1f4da; &#x1f308; 个人主页&#xff1a;高斯小哥 &#x1f525; 高质量专栏&#xff1a;Matplotlib之旅&#xff1a;零基础精通数据可视化、Python基础【高质量合集】、PyTorch零基础入门教程 &#x1f448; 希望得到您的…

本地数据库Room——study_1

目录 1.概念 2.组成 3.导入依赖 4.具体实现 4.1 数据表的设置 4.2 方法接口 4.3 数据库类 -》 基石&#xff0c;使用模板 4.4 实现真正的实例Room库 1.概念 Room 持久性库在 SQLite 上提供了一个抽象层&#xff0c;以便在充分利用 SQLite 的强大功能的同时&#xff0c…

Qt程序设计-钟表自定义控件实例

本文讲解Qt钟表自定义控件实例。 效果如下: 创建钟表类 #ifndef TIMEPIECE_H #define TIMEPIECE_H#include <QWidget> #include <QPropertyAnimation> #include <QDebug> #include <QPainter> #include <QtMath>#include <QTimer>#incl…

基于InternLM和LangChain搭建自己的知识库

背景 LLM存在一定的局限性&#xff0c;如&#xff1a; 知识时效性受限&#xff1a;如何让LLM能够获取最新的知识专业能力有限&#xff1a;如何打造垂直领域的大模型定制化成本高&#xff1a;如何打造个人专属的LLM应用 正文 为了突破LLM的局限性&#xff0c;目前有两种范式…

51单片机(6)-----直流电机的介绍与使用(通过独立按键控制电机的运行)

前言&#xff1a;感谢您的关注哦&#xff0c;我会持续更新编程相关知识&#xff0c;愿您在这里有所收获。如果有任何问题&#xff0c;欢迎沟通交流&#xff01;期待与您在学习编程的道路上共同进步。 目录 一. 直流电机模块介绍 1.直流电机介绍 2.电机参数 二. 程序设计…