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

news2024/12/23 9:41:33

目录

一、堆的定义

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.coloradmin.cn/o/1394385.html

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

相关文章

2024 年 10 款最佳 Windows 免费分区管理器软件

买了一台现成的全新电脑&#xff0c;出于多种原因希望对硬盘进行分区&#xff0c;例如&#xff0c;为了更好地组织文件。我们整理了一份最佳分区软件列表&#xff0c;可以帮助您轻松完成这项任务。 适用于 Windows 11/10/8.1/8/7 的最佳 10 个磁盘分区工具 1.奇客分区大师 兼容…

vue3自定义按钮点击变颜色(切换)

实现效果图&#xff1a; 默认选中第一个按钮&#xff0c;未选中按钮为粉色&#xff0c;点击时颜色变为红色 利用动态类名&#xff0c;当定义isChange数值和下标index相同时&#xff0c;赋予act类名&#xff0c;实现变色效果 <template><view class"page"&g…

FPGA 多路分频器实验

1 概述 在 FPGA 中&#xff0c;时钟分频是经常用到的。本节课讲解 2 分频、3 分频、4 分频和 8 分频的 Verilog 实现并且学习 generate 语法功能的应。 2 程序设计思路 1&#xff09;整数倍分频&#xff0c;为 2、4、8&#xff0c;这种 2^n 次方倍数倍数关系的…

Spring Security 中 Authentication和Authorization的区别

Authentication Spring Security提供了全面的认证支持。认证是用来验证试图访问特定资源的用户身份的方式。验证用户的常见方式是要求用户输入用户名和密码。一旦认证完成&#xff0c;我们就知道了用户的身份并且可以进行授权。 Spring Security内置支持对用户进行认证。 简…

el-date-picker如果超过限制跨度则提示

需求&#xff1a;实现日期时间选择组件跨度如果超过限制天数&#xff0c;点击查询则提示超过限制时间 封装一个方法&#xff0c;传入开始和结束时间以及限制天数&#xff0c;如果超过则返回false //计算时间跨度是否超过限制天数isTimeSpanWithinLimit(startTime, endTime, li…

Android Text View 去掉默认的padding的实现方法

先看下最终实现效果&#xff0c;满意您在往下看&#xff1a; TextView 绘制的时候自带一定的Padding值&#xff0c;要想实现去掉默认的padding值&#xff0c;xml文件可以设置一个属性值 &#xff1a; android:includeFontPadding"false" 然后运行起来就会发现&…

【C++干货铺】红黑树 (Red Black Tree)

个人主页点击直达&#xff1a;小白不是程序媛 C系列专栏&#xff1a;C干货铺 代码仓库&#xff1a;Gitee 目录 前言 红黑树的概念 红黑树的性质 红黑树结点的定义 红黑树的插入操作 插入新的结点 检查规则进行改色 情况一 情况二 情况三 插入完整代码 红黑树的验…

ArcGIS Pro 如何新建布局

你是否已经习惯了在ArcGIS中数据视图和布局视图之间来回切换&#xff0c;到了ArcGIS Pro中却找不到二者之间切换的按钮&#xff0c;即使新建布局后却发现地图怎么却是一片空白。 这一切的一切都是因为ArcGIS Pro的功能框架完全不同&#xff0c;这里为大家介绍一下在ArcGIS Pro…

纸黄金实战投资技巧:避免亏损的有效策略

在纸黄金交易的实战中&#xff0c;避免亏损是每位投资者都追求的目标。虽然任何投资都存在一定的风险&#xff0c;但采取一些有效的策略可以帮助投资者最大限度地减少亏损的可能性。以下是一些在纸黄金交易中避免亏损的实战技巧&#xff1a; 一、设定止损点是避免亏损的关键 止…

【Android】自定义View onDraw()方法会调用两次

问题 自定义了View后&#xff0c;在构造函数中设置画笔颜色&#xff0c;发现它没起效&#xff0c;但是在onDraw()里设置颜色就会起效&#xff0c;出问题的代码如下&#xff1a; public RoundSeekbarView(Context context, Nullable AttributeSet attrs) {super(context, attrs…

dubbo入门案例!!!

入门案例之前我们先介绍一下&#xff1a;zookeeper。 Zookeeper是Apacahe Hadoop的子项目&#xff0c;可以为分布式应用程序协调服务&#xff0c;适合作为Dubbo服务的注册中心&#xff0c;负责服务地址的注册与查找&#xff0c;相当于目录服务&#xff0c;服务提供者和消费者只…

考下初级会计证书,好处竟有这么多!柯桥学会计去哪里?零基础入门手把手教学

初级会计证书有什么用&#xff1f; 初级会计证书有什么用&#xff1f;往下看&#xff0c;看完还没报名的建议大家赶紧报名&#xff0c;今年拿下&#xff01;因为初级会计证书真的很有用&#xff01; 01 求职刚需 初级会计是会计职业的起点&#xff0c;很多会计基础岗位&#x…

rust跟我学六:虚拟机检测

图为RUST吉祥物 大家好,我是get_local_info作者带剑书生,这里用一篇文章讲解get_local_info是怎么检测是否在虚拟机里运行的。 首先,先要了解get_local_info是什么? get_local_info是一个获取linux系统信息的rust三方库,并提供一些常用功能,目前版本0.2.4。详细介绍地址:…

关于Jenkins安装后,插件管理中插件版本依赖报错问题的解决方法

我们在初次安装完Jenkins后&#xff0c;通常会去下载要使用的插件&#xff0c;但是在插件管理中通常会出现插件版本问题的提示&#xff0c;例如&#xff1a; 此类问题一般可通过升级Jenkins到最新版本来解决问题。但是Jenkins从旧版本升级到最新版本&#xff0c;望望可能会连…

redis安装-Linux为例

可以下载一个Shell或者MobaXterm工具&#xff0c;便于操作 在redis官网下载压缩包 开始安装 安装依赖 yum install -y gcc tcl切换目录 切换目录后直接把redis安装包拖到/user/local/src/下 cd /user/local/src/解压然后安装 #解压 tar -zxvf redis-7.2.4.tar.gz #安装 …

并发编程之MESI缓存一致性协议

目录 CPU缓存架构 CPU多核缓存架构 CPU缓存架构缓存一致性的解决方案 缓存一致性协议实现原理 总线窥探 工作原理 窥探协议类型 缓存一致性协议 MESI协议 伪共享问题 CPU缓存架构 CPU缓存即高速缓冲存储器&#xff0c;是位于CPU与主内存间的一种容量较小但速度很高的…

如何在MinIO存储服务中通过Buckets实现远程访问管理界面上传文件

文章目录 前言1. 创建Buckets和Access Keys2. Linux 安装Cpolar3. 创建连接MinIO服务公网地址4. 远程调用MinIO服务小结5. 固定连接TCP公网地址6. 固定地址连接测试 前言 MinIO是一款高性能、分布式的对象存储系统&#xff0c;它可以100%的运行在标准硬件上&#xff0c;即X86等…

Halcon基于相关性的模板匹配

Halcon基于相关性的模板匹配 基于相关性的模板匹配其实是另一种基于灰度值的匹配&#xff0c;不过它的特点是使用一种归一化的互相关匹配&#xff08;Normalized Cross Correlation&#xff0c;NCC&#xff09;来衡量模板图像和检测图像之间的关系&#xff0c;因此&#xff0c…

前端实现轮训和长连接

简介 轮训和长连接相关内容可以参考之前的文章消息推送系统。消息推送系统-CSDN博客文章浏览阅读106次。在餐饮行业中&#xff0c;店内应用有pos、厨显屏等&#xff0c;云端应用为对应数据中心。为了实现云端数据和操作指令下发到店内应用&#xff0c;需要有一个系统实现这个功…

“重大利好”!以太坊坎昆升级临近!Layer2新玩家Blast不断蚕食市场份额,令竞品汗流浃背?

1月17日&#xff0c;坎昆升级&#xff08;Dencun&#xff09;率先在以太坊Goerli测试网启动&#xff0c;由于Goerli是参与者数量以及网络负载程度最高的测试网&#xff0c;仅次于以太坊主网&#xff0c;因此如果没什么问题&#xff0c;预示着主网升级已经不远。 而现在&#xf…