【数据结构】堆:堆的构建,堆的向上调整算法,堆的向下调整算法、堆排序

目录

一、堆的定义

1、堆的定义:

2、根节点与其左、右孩子间的联系

  二、堆的创建

1、堆的向下调整算法 

2、堆的向上调整算法 

三、堆排序


一、堆的定义

1、堆的定义:

堆可以被看作是一棵完全二叉树数组对象。即在存储结构上是数组,在逻辑结构上是一棵完全二叉树。在堆中,树的每个节点都满足堆属性,即父节点的值大于(或小于)其子节点的值。

具体而言,对于最大堆,父节点的值大于等于其子节点的值;而对于最小堆,则是父节点的值小于等于其子节点的值。这使得堆的根节点(常常是数组的第一个元素)成为堆中最大(或最小)的元素。


2、根节点与其左、右孩子间的联系

在堆中,根节点和其子节点之间存在一种特殊的联系。

        对于任意一个节点 i,其左子节点位于位置 2i+1,右子节点位于位置 2i+2。反之,对于任意一个节点 j,其父节点位于位置 (j-1)/2。(这里的位置是指在数组中的索引位置)

        换句话说,如果我们将堆表示为一个数组,那么对于任意节点 i,其左子节点就是数组中下标为 2i+1 的元素,右子节点就是数组中下标为 2i+2 的元素。而节点 i 的父节点就是数组中下标为 (i-1)/2 的元素。


二、堆的创建

下面我们给出一个数组,这个数组逻辑上可以看做一颗完全二叉树,但是还不是一个堆,现在我们通过算法,把它构建成一个堆。根节点左右子树不是堆,我们怎么调整呢?这里我们介绍两种方法:堆的向下调整算法堆的向上调整算法

1、堆的向下调整算法 

这里我们从倒数的第一个非叶子节点的子树开始调整,一直调整到根节点的树,就可以调整成堆。

具体步骤如下:

  1. 首先,根据二叉树的性质,最后一个非叶子节点的索引可以通过 (n-2)/2 计算得到,其中 n 是二叉树的节点总数。

  2. 我们可以使用一个循环,从最后一个非叶子节点开始,依次向前遍历每个节点。

  3. 对于每个节点,我们可以调用 AdjustDown 函数来对其进行向下调整的操作。

  4. 通过依次向上调整每个节点,我们可以确保整个二叉树满足堆的性质。

 

代码及注释:  

#include <stdio.h>
#include<stdlib.h>void swap(int* a, int* b)
{int temp;temp = *a;*a = *b;*b = temp;
}void AdjustDown(int* a, int n, int root)
{int child; // 子节点的索引child = root * 2 + 1; // 计算左孩子节点的索引while (child < n) // 当存在孩子节点时循环{if (child + 1 < n && a[child + 1] > a[child]) // 如果存在右孩子且右孩子大于左孩子{child++; // 将 child 置为右孩子的索引}if (a[root] < a[child]) // 如果根节点小于孩子节点{swap(&a[root], &a[child]); // 交换根节点和孩子节点的值}root = child; // 将根节点更新为孩子节点child = root * 2 + 1; // 计算新根节点的左孩子节点索引}
}int main()
{int a[] = {10,1,3,2,4,6,8,9,7};int n = sizeof(a) / sizeof(int);int root = (n - 2) / 2;int i;for (i = root; i >= 0; i--){AdjustDown(a, n, i); // 对每个根节点进行向下调整操作}for (i = 0; i < n; i++){printf("%d ", a[i]);}return 0;
}

2、堆的向上调整算法 

  • 开始时,我们将数组第一个元素视为一个堆。
  • 对于后续每个元素,调用 AdjustUp 函数进行向上调整的操作,使当前包含的数组元素满足堆的性质——即向堆中插入数据并通过向上调整使其成为新的堆。
  • AdjustUp 函数比较元素的值与其父节点的值,如果子节点的值大于父节点的值,则交换两个节点的值,并把 child 更新为其父节点的索引,继续向上比较。
  • 这样循环调整每个元素,可以使整个数组满足堆的性质。

代码及注释:  

#include<stdio.h>
#include<stdlib.h>
#include<assert.h>typedef int HPDataType;#include <stdio.h>
#include <assert.h>// 交换两个元素的值
void Swap(int* a, int* b)
{int temp;temp = *a;*a = *b;*b = temp;
}// 向上调整操作
void AdjustUp(int* a, int child)
{assert(a);int parent;while (child > 0){parent = (child - 1) / 2;  // 计算父节点位置if (a[child] > a[parent])  // 如果子节点的值大于父节点的值{Swap(&a[child], &a[parent]);  // 交换两个节点的值child = parent;  // child 更新为 parent,继续向上比较}else{break;}}
}int main()
{int a[] = {10, 1, 3, 2, 4, 6, 8, 9, 7};int n = sizeof(a) / sizeof(int);int i;for (i = 0; i < n; i++){AdjustUp(a, i);  // 对每个节点进行向上调整操作}for (i = 0; i < n; i++){printf("%d ", a[i]);  // 输出调整后的数组}return 0;
}

三、堆排序

堆排序是一种基于二叉堆(heap)数据结构的排序算法。它的思想可以概括为以下几个步骤:

  1. 构建堆:将待排序的数组视为一个完全二叉树,并将其转化为一个堆。这可通过从最后一个非叶子节点开始,逐个向上调整每个节点来完成。调整操作会使得当前节点和其子树满足堆的性质,即父节点的值大于等于(或小于等于)其子节点的值。这样就构建了一个最大堆(或最小堆)。

  2. 排序:经过构建堆操作后,堆顶元素是最大(或最小)的元素。我们可以将堆顶元素与堆中最后一个元素交换位置,然后将堆的大小减小 1。这样,最大(或最小)的元素会被放置到正确的位置(即最后一个位置)。接着,我们对堆顶元素进行向下调整,使得堆再次满足堆的性质。重复以上步骤,直到堆中只剩下一个元素。

  3. 返回有序序列:当堆中只剩下一个元素时,所有的元素都已经交换并放置到了正确的位置。此时,我们就得到了一个有序的序列。

堆排序的时间复杂度为 O(nlogn),其中 n 是数组的大小。它是一种原址排序算法,因为它只需要用到原始数组,不需要使用额外的空间。同时,堆排序也是一种稳定的排序算法。

代码及注释: 

void AdjustDown(int* a, int parent, int n)
{// 计算左子节点的索引int child = parent * 2 + 1;// 当左子节点在数组范围内时进行循环while (child < n){// 如果右子节点存在且比左子节点大,则选择右子节点作为与父节点进行比较的子节点if (child + 1 < n && a[child] < a[child + 1]) {child++;}// 如果父节点小于子节点,则交换它们的值if (a[parent] < a[child]) {swap(&a[parent], &a[child]);// 更新父节点和子节点的索引parent = child;child = parent * 2 + 1;}else{break;}}
}void HeapSort(int* a, int n)
{// 从最后一个非叶子节点开始,依次调用 AdjustDown 函数,构建最大堆int i = 0;int end = n / 2 - 1;for (i = end; i >= 0; i--){AdjustDown(a, i, n);}// 交换堆顶元素与最后一个元素,并向下调整堆for (i = 0; i < n; i++){swap(&a[0], &a[n - i - 1]);AdjustDown(a, 0, n - i - 1);}
}

 

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【每周AI简讯】GPT-5将有指数级提升,GPT Store正式上线

AI7 - Chat中文版最强人工智能 OpenAI的CEO奥特曼表示GPT-5将有指数级提升 GPT奥特曼参加Y-Combinator W24启动会上表示&#xff0c;我们已经非常接近AGI。GPT-5将具有更好的推理能力、更高的准确性和视频支持。 GPT Store正式上线 OpenAI正式推出GPT store&#xff0c;目前…

【STM32】HAL库的STOP低功耗模式UART串口唤醒,解决首字节出错的问题(全网第一解决方案)

【STM32】HAL库的STOP低功耗模式UART串口唤醒&#xff0c;解决首字节出错的问题&#xff08;全网第一解决方案&#xff09; 前文&#xff1a; 【STM32】HAL库的STOP低功耗模式UART串口唤醒&#xff0c;第一个接收字节出错的问题&#xff08;疑难杂症&#xff09; 目前已解决 …

设计模式—— 单例设计模式

单例设计模式 什么是单例模式 单例模式是一种对象创建型模式&#xff0c;使用单例模式&#xff0c;可以保证为一个类只生成唯一的实例对象。也就是说&#xff0c;在整个程序空间中&#xff0c;该类只存在一个实例对象。 为什么使用单例模式 在应用系统开发中&#xff0c;我…

51单片机_智能家居终端

实物演示效果&#xff1a; https://www.bilibili.com/video/BV1bh4y1A7ZW/?vd_source6ff7cd03af95cd504b60511ef9373a1d 51单片机是否适合做多功能智能家居控制系统&#xff1f;51单片机的芯片是否具有与WiFi通信的能力&#xff1f;如果有的话&#xff0c;具体有哪些芯片啊&a…

网工每日一练(1月15日)

1.某计算机系统由下图所示的部件构成&#xff0c;假定每个部件的千小时可靠度为R&#xff0c;则该系统的千小时的可靠度为 ( D ) 。 2.以下IP地址中&#xff0c;属于网络 201.110.12.224/28 的主机IP是&#xff08; B &#xff09;。 A.201.110.12.224 B.201.110.12.238 C.20…

【JavaEE】文件操作: File 类的用法和 InputStream, OutputStream 的用法

目录 1. File 概述 1.1 File的属性 1.2 File的构造方法 1.3 File的方法 2.读文件 2.1 InputStream 概述 2.2 FileInputStream 概述 2.3 正确打开和关闭文件的方式 2.4 不同方式读取文件代码示例 2.4 另一种方法:利用 Scanner 进行字符读取 3.写文件 3.1 OutputStre…

滚动菜单ListView

activity_main.xml <include layout"layout/title"/> 引用上章自定义标题栏 <?xml version"1.0" encoding"utf-8"?> <LinearLayout xmlns:android"http://schemas.android.com/apk/res/android"xmlns:app&qu…

Rust之构建命令行程序(三):重构改进模块化和错误处理

开发环境 Windows 10Rust 1.74.1 VS Code 1.85.1 项目工程 这次创建了新的工程minigrep. 重构改进模块化和错误处理 为了改进我们的程序&#xff0c;我们将修复与程序结构及其处理潜在错误的方式有关的四个问题。首先&#xff0c;我们的main函数现在执行两项任务:解析参数和…

使用pdfbox 为 PDF 增加水印

使用pdfbox 为 PDF增加水印https://www.jylt.cc/#/detail?activityIndex2&idbd410851b0a72dad3105f9d50787f914 引入依赖 <dependency><groupId>org.apache.pdfbox</groupId><artifactId>pdfbox</artifactId><version>3.0.1</ve…

如何卸载旧版docker

环境&#xff1a; Docker1.13 centos7.6 问题描述&#xff1a; 如何卸载旧版docker 解决方案&#xff1a; 1.停止Docker服务。使用以下命令停止Docker服务&#xff1a; sudo service docker stop2.卸载Docker软件包。根据您的Linux发行版&#xff0c;使用适当的包管理器来…

Qt SDL2播放Wav音频

这里介绍两种方法来实现Qt播放Wav音频数据。 方法一&#xff1a;使用QAudioOutput pro文件中加入multimedia模块。 #include <QApplication> #include <QFile> #include <QAudioFormat> #include <QAudioOutput>int main(int argc, char *argv[]) {…

P4学习(四)实验一:Basic Forwarding

目录 一.前置知识二.实验过程记录1.找到实验文件2.拓扑图3.明确实验内容4.实验初体验 三. 编写解决方案1.Parse部分1.1 Code1.2 知识点解析 2.Ingress部分2.1 Code2.2 知识点解析 3.Deparse部分3.1 Code3.2 知识点 四.实验完成测试 一.前置知识 Linux基础命令(vim)V!Model的架…

kibana查看和展示es数据

本文来说下使用kibana查看和展示es数据 文章目录 数据准备查询所有文档示例kibana查看和展示es数据 数据准备 可以使用es的命令或者java程序来往&#xff0c;es进行新增数据 查询所有文档示例 在 apifox 中&#xff0c;向 ES 服务器发 GET请求 &#xff1a;http://localhost:92…

【Qt-license】误操作qt下载导致只能安装商业版试用十天,无法安装社区版

背景&#xff1a; 原本是为了学习qml&#xff0c;需要下载一个design studio&#xff0c;而这个需要比较新版的安装程序&#xff0c;但新版的安装程序官方都是online安装。于是从官网找下载链接。毕竟是英文的&#xff0c;又心急&#xff0c;误打误撞中我选择了商业版试用。 其…

线程安全的集合类

Java中提供了许多集合类&#xff0c;其中有的是线程安全的&#xff0c;有的是线程不安全的。线程安全的集合类有&#xff1a; 1. Vector&#xff1a;Vector类实现了一个动态数组&#xff0c;与ArrayList相似&#xff0c;但Vector是同步访问的 2. Stack&#xff1a;Stack是Vec…

血糖仪定制_基于联发科MT6761平台的血糖尿酸检测仪解决方案

高尿酸血症和糖尿病患者的发病都受到遗传因素和相同的饮食习惯的影响;高尿酸血症患者往往也是糖尿病的高发人群。糖尿病患者常常伴有肥胖、胰岛素抵抗等症状&#xff0c;这些都会影响尿酸的代谢。因此&#xff0c;在预防高尿酸的同时也需要预防高血糖的发生。 为了方便高尿酸人…

Unity关于纹理图片格式带来的内存问题和对预制体批量格式和大小减半处理

我们经常会遇到内存问题&#xff0c;这次就是遇到很多图片的默认格式被改成了RGB32&#xff0c;导致Android打包后运行内存明显增加。 发生了什么 打包Android后&#xff0c;发现经常崩溃&#xff0c;明显内存可能除了问题&#xff0c;看了内存后发现了问题。 见下图&#xf…

使用composer生成的DMG和PKG格式软件包有何区别

在使用Composer从包源构建软件包时候&#xff0c;有两种不同类型的包&#xff1a;PKG和DMG。你知道两者之间的区别吗? 以及如何选取吗&#xff1f; 每种格式都有各自的优势具体取决于软件包的预期用途以及用于部署软件包的工具。下面我们来了解一下PKG和DMG格式的区别和用途。…

Puppeteer让你网页操作更简单(1)屏幕截图

网页自动化设计爬虫工具 中就使用了Puppeteer进行对网页自动化处理&#xff0c;今天就来看看它是什么东西&#xff01; 我们将学习什么? 在本教程中,您将学习如何使用JavaScript自动化和抓取 web。 为此,我们将使用Puppeteer。 Puppeteer是一个Node库API,允许我们控制无头Ch…

STM32项目设计:人脸识别门禁系统

文章目录 项目简介硬件设计 项目视频链接&#xff1a;【还在制作中&#xff0c;制作好会发在哔哩哔哩&#xff1a;化作尘my&#xff0c;记得先关注】 项目实物链接&#xff1a;【可以看看某鱼&#xff1a;化作尘my】 有需要可以购买一个实物&#xff0c;会提供相应的参考资料学…