[数据结构]——二叉树——堆的实现

1. 堆的概念及结构

如果有一个关键码的集合K = { , , ,…, },把它的所有元素按完全二叉树的顺序存储方式存储
在一个一维数组中,并满足: <= 且 <= ( >= 且 >= ) i = 0,1,
2…,则称为小堆(或大堆)。将根节点最大的堆叫做最大堆或大根堆,根节点最小的堆叫做最小堆或小根堆。
堆的性质:
堆中某个节点的值总是不大于或不小于其父节点的值;
堆总是一棵完全二叉树。

2. 堆的实现

1.堆的创建


下面我们给出一个数组,这个数组逻辑上可以看做一颗完全二叉树,但是还不是一个堆,现在我们通过算法,把它构建成一个堆。根节点左右子树不是堆,我们怎么调整呢?这里我们从倒数的第一个非叶子节点的子树开始调整,一直调整到根节点的树,就可以调整成堆。

int a[] = {1,5,3,8,7,6};

2. 建堆时间复杂度


因为堆是完全二叉树,而满二叉树也是完全二叉树,此处为了简化使用满二叉树来证明(时间复杂度本来看的就是近似值,多几个节点不影响最终结果):

因此:建堆的时间复杂度为O(N)。 

3.堆的插入


先插入一个10到数组的尾上,再进行向上调整算法,直到满足堆。

 4.堆的删除


删除堆是删除堆顶的数据,将堆顶的数据根最后一个数据一换,然后删除数组最后一个数据,再进行向下调整算法。

 5.堆向下调整算法

                                                                                                


现在我们给出一个数组,逻辑上看做一颗完全二叉树。我们通过从根节点开始的向下调整算法可以把它调整成一个小堆。向下调整算法有一个前提:左右子树必须是一个堆,才能调整。

 3.代码深度解析

1.首先弄一个交换数据

 交换两个指针所指向的变量的值

通过解引用操作符*,将p2指针所指向的变量的值赋给了p1指针所指向的变量,将之前存储在temp中的值赋给了p2指针所指向的变量,完成了交换。

void Swap(HPDataType *p1, HPDataType *p2)
{HPDataType temp = *p1;*p1 =*p2;*p2 = temp;
}

2.向上调整堆

将数组a中指定索引child的元素向上调整,使其在最小堆中满足最小堆的性质

通过计算child的父节点索引,即(parent = (child - 1) / 2),确定了父节点的位置。在循环中,child的元素比父节点的元素小调用Swap函数,将child和parent指向的元素进行交换。然后,代码更新child和parent的值,将child变为parent,parent变为(child - 1) / 2,继续循环。如果不是,即child的元素不小于父节点的元素,代码通过break语句跳出循环,这时已经完成了向上调整的操作。

void AdjudtUp(HPDataType* a, int child)
{int parent = (child - 1) / 2;while (child > 0){if (a[child] < a[parent]){Swap(&a[child], &a[parent]);child = parent;parent = (child - 1) / 2;}else{break;}}
}

3.向下调整堆

调整小顶堆的算法,接受一个int类型的指针a,表示一个数组,size表示数组的大小, parent表示要调整的节点位置

void AdjustDown(int* a, int size, int parent)
{int child = parent * 2 + 1;while (child < size){if (child + 1 < size && a[child + 1] < a[child]){++child;}if (a[child] < a[parent]){Swap(&a[child], &a[parent]);parent = child;child = parent * 2 + 1;}else{break;}}
}

4.堆的插入

堆已满,则需要扩容。然后,将原来的内存空间指针hp->_a指向新分配的内存空间,更新堆的容量为新的容量。将要插入的元素x赋值给堆的最后一个位置hp->_a[hp->_size],然后增加堆的大小hp->_size++。最后,调用AdjustUp函数将新插入的元素向上调整,以维护堆的性质

void HeapPush(Heap* hp, HPDataType  x)
{assert(hp);if (hp->_size == hp->_capacity){int newCapacity = hp->_capacity == 0 ? 4: hp->_capacity * 2;HPDataType*temp = (HPDataType*)realloc(hp->_a, newCapacity * sizeof(HPDataType));if (temp == NULL){perror("realloc fail");exit(-1);}hp->_a =temp;hp->_capacity = newCapacity;} hp->_a[hp->_size] = x;hp->_size++;AdjudtUp(hp->_a,hp->_size-1);
}

5.堆的创建

初始化后再通过for循环遍历数组a,并调用HeapPush函数将数组中的元素依次插入到堆中

void HeapCreate(Heap* hp, HPDataType *a, int n )
{assert(hp);hp->_size = 0; hp->_capacity =NULL; // 将数组a中的元素依次插入堆中for (int i = 0; i < n; i++) {HeapPush(hp, a[i]);}
}

6.堆的销毁

使用free函数释放堆的数组hp->_a的内存空间

void HeapDestory(Heap* hp)
{assert(hp);free(hp->_a);hp->_a = NULL;hp->_size =hp->_capacity=0;}

7.堆的删除

调用AdjustDown()函数,从堆顶开始向下调整堆,以保持堆的性质

void HeapPop(Heap* hp)
{assert(hp);assert(hp->_size > 0);Swap(&hp->_a[0], &hp->_a[hp->_size - 1]);hp->_size--;AdjustDown(hp->_a, hp->_size, 0);
}

8.取堆顶的数据


函数返回堆中数组_a的第一个元素即堆顶的数据

取堆顶的数据
HPDataType HeapTop(Heap* hp)
{assert(hp);assert(hp->_size > 0);return hp->_a[0];
}

9.堆的数据个数

返回堆的_size成员,即堆的大小

int HeapSize(Heap* hp)
{assert(hp);return hp->_size;
}



10. 堆的判空

  1. 通过断言assert(hp);来确保传入的参数hp不为NULL。

  2. 然后,通过判断堆的大小hp->_size是否为0来判断堆是否为空。

  3. 如果堆的大小为0,则返回1(即堆为空),否则返回0(即堆不为空)

int HeapEmpty(Heap* hp)
{assert(hp);return hp->_size == 0;
}

4.总的代码

1.heap.h

#pragma once
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
#include<stdbool.h>
typedef int HPDataType;
typedef struct Heap
{HPDataType* _a;int _size;int _capacity;
}Heap;
//数据交换
void Swap(HPDataType* p1, HPDataType* p2);
//向上堆的调整
void AdjudtUp(Heap* _a, int child);
// 向下调整堆
void AdjustDown(int* a, int size, int parent);
// 堆的构建
//void HeapCreate(Heap* hp);//, HPDataType* a, int nvoid HeapCreate(Heap* hp, HPDataType* a, int n);
// 堆的销毁
void HeapDestory(Heap* hp);
// 堆的插入
void HeapPush(Heap* hp, HPDataType x);
// 堆的删除
void HeapPop(Heap* hp);
// 取堆顶的数据
HPDataType HeapTop(Heap* hp);
// 堆的数据个数
int HeapSize(Heap* hp);
// 堆的判空
int HeapEmpty(Heap* hp);

2.Heap.c

#include"Heap.h"
//交换数据
void Swap(HPDataType *p1, HPDataType *p2)
{HPDataType temp = *p1;*p1 =*p2;*p2 = temp;
}
//向上调整堆
void AdjudtUp(HPDataType* a, int child)
{int parent = (child - 1) / 2;while (child > 0){if (a[child] < a[parent]){Swap(&a[child], &a[parent]);child = parent;parent = (child - 1) / 2;}else{break;}}
}
// 向下调整堆
void AdjustDown(int* a, int size, int parent)
{int child = parent * 2 + 1;while (child < size){if (child + 1 < size && a[child + 1] < a[child]){++child;}if (a[child] < a[parent]){Swap(&a[child], &a[parent]);parent = child;child = parent * 2 + 1;}else{break;}}
}
// 堆的插入
void HeapPush(Heap* hp, HPDataType  x)
{assert(hp);if (hp->_size == hp->_capacity){int newCapacity = hp->_capacity == 0 ? 4: hp->_capacity * 2;HPDataType*temp = (HPDataType*)realloc(hp->_a, newCapacity * sizeof(HPDataType));if (temp == NULL){perror("realloc fail");exit(-1);}hp->_a =temp;hp->_capacity = newCapacity;} hp->_a[hp->_size] = x;hp->_size++;AdjudtUp(hp->_a,hp->_size-1);
}
// 堆的构建
//void HeapCreate(Heap* hp)//HPDataType* a, int n
//{
//    assert(hp);
//    hp->_size = 0; // 初始化堆的大小为0
//    hp->_capacity = 0; // 设置堆的容量为n
//    hp->_a = NULL;
//
//     将数组a中的元素依次插入堆中
//    //for (int i = 0; i < n; i++) {
//    //    HeapPush(&hp, a[i]);
//    //}
//}
void HeapCreate(Heap* hp, HPDataType *a, int n )
{assert(hp);hp->_size = 0; // 初始化堆的大小为0hp->_capacity =NULL; // hp->_a = NULL;// 将数组a中的元素依次插入堆中for (int i = 0; i < n; i++) {HeapPush(hp, a[i]);}
}
// 堆的销毁
void HeapDestory(Heap* hp)
{assert(hp);free(hp->_a);hp->_a = NULL;hp->_size =hp->_capacity=0;}
// 堆的删除
void HeapPop(Heap* hp)
{assert(hp);assert(hp->_size > 0);Swap(&hp->_a[0], &hp->_a[hp->_size - 1]);hp->_size--;AdjustDown(hp->_a, hp->_size, 0);
}
// 取堆顶的数据
HPDataType HeapTop(Heap* hp)
{assert(hp);assert(hp->_size > 0);return hp->_a[0];
}
// 堆的数据个数
int HeapSize(Heap* hp)
{assert(hp);return hp->_size;
}
// 堆的判空
int HeapEmpty(Heap* hp)
{assert(hp);return hp->_size == 0;
}

2.Test.c

#include"Heap.h"
void test()                          
{Heap sl;int a[10] = { 10,8,2,4,5,3,6,7,9,1 };/*HeapCreate(&sl);for (int i = 0;i < sizeof(a) / sizeof(int); i++){HeapPush(&sl, a[i]);}*/HeapCreate(&sl,a, sizeof(a) / sizeof(int));while (!HeapEmpty(&sl))	{printf("%d  ", HeapTop(&sl));HeapPop(&sl);}printf("\n");
}
int main()
{test();return 0;
}

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

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

相关文章

前端面试题(1)

1&#xff0c;CSS盒子模型 CSS的盒模型有两种&#xff1a;标准盒模型&#xff0c;IE盒模型。IE盒模型&#xff0c;其中content包含了内边距padding和边框border。盒子的实际宽度 contentmargin。标准盒模型&#xff0c;盒子的实际宽度 content(内容)padding(内边距)border(边…

rocky9 yum 安装与配置MySQL8

1.前置条件&#xff1a; 把yum包更新到最新 [rootlocalhost ~]# yum update 查看系统中是否已安装 MySQL 服务 rpm -qa|grep mysql 如果有安装mysql,则需要先卸载之前安装的mysql&#xff1a;yum -y remove mysql 然后再查看mysql是否都卸载完成,如果还有没卸载完成的&am…

小程序开发SSL证书下载和安装

在开发小程序时&#xff0c;确保数据的安全传输至关重要&#xff0c;而实现这一目标的关键在于正确获取与安装SSL证书。以下详细介绍了从获取到安装SSL证书的完整流程&#xff0c;以助您为小程序构建可靠的加密通信环境。 一、小程序SSL证书类型选择&#xff1a; 域名验证型D…

创新指南|全球需求低迷中国企业出海趋势洞察和创新机会

对于企业出海来说&#xff0c;第一步判断趋势非常重要&#xff0c;这甚至事关企业生死。比如十年前的2014年&#xff0c;中国最大的两家电商平台阿里和京东成功IPO&#xff08;上市&#xff09;后&#xff0c;认为接下来最大的机会在于中国市场的消费升级。与阿里、京东不同&am…

TypeScript基础语法

这里写自定义目录标题 变量条件控制循环函数类和接口模块开发 变量 TypeScript在JavaScript的基础上加入了静态类型检查功能&#xff0c;因此每一个变量都有固定的数据类型。 let msg: string hello worldlet 声明变量的关键字&#xff0c; const 则代表常量 msg 变量名称 &…

Linux:软件包管理器 - yum

Linux&#xff1a;软件包管理器 - yum Linux的软件安装方式源代码安装rpm包安装yum安装 yum三板斧yum listyum installyum remove yum生态yum源 Linux的软件安装方式 源代码安装 在Linux下安装软件, 一个通常的办法是下载到程序的源代码, 并进行编译, 得到可执行程序 源代码安…

git知识

如何将develop分支合并到master分支 #简单版 git checkout master git pull origin master git merge origin/develop # 解决可能的冲突并提交 git push origin master#复杂版 git checkout master # 拉取远程 master 分支的最新代码并合并到本地 git pull origin master # 拉…

新时代·高质量·硬道理丨开放的大门越开越大、开放的水平越来越高

新时代下&#xff0c;中国坚定不移地实施扩大高水平对外开放战略&#xff0c;致力于构建更高层次、更宽领域的开放型经济体系。以下是对新时代高质量硬道理这一主题下&#xff0c;中国开放大门越开越大、开放水平越来越高的几个关键点分析&#xff1a; 全方位开放格局 政府工…

YOLOv8打印模型结构配置信息并查看网络模型详细参数:参数量、计算量(GFLOPS)

《博主简介》 小伙伴们好&#xff0c;我是阿旭。专注于人工智能、AIGC、python、计算机视觉相关分享研究。 ✌更多学习资源&#xff0c;可关注公-仲-hao:【阿旭算法与机器学习】&#xff0c;共同学习交流~ &#x1f44d;感谢小伙伴们点赞、关注&#xff01; 《------往期经典推…

计算机基础知识-第7章-程序的本质(2)——算法与数据结构概论

一、算法数据结构程序 提出这一公式并以此作为其一本专著的书名的瑞士计算机科学家尼克劳斯沃思&#xff08;Niklaus Wirth&#xff09;由于发明了多种影响深远的程序设计语言&#xff0c;并提出结构化程序设计这一革命性概念而获得了1984年的图灵奖。他是至今惟一获此殊荣的瑞…

Java——测试相关

1、测试的常用方法 单元测试&#xff1a;针对软件中最小可测试单元进行的测试&#xff0c;如类、方法等。功能测试&#xff1a;检查软件的各项功能是否按照需求规格书执行&#xff0c;通常包括正常功能、边界情况和异常情况的测试。性能测试&#xff1a;检查软件的速度、响应时…

【Linux杂货铺】文件系统

目录 &#x1f308;前言&#x1f308; &#x1f4c1; 硬盘 &#x1f4c2; 物理结构 &#x1f4c2; 存储结构 &#x1f4c2; CHS定址法 &#x1f4c2; 操作系统对硬盘的管理和抽象 &#x1f4c1; 文件系统 &#x1f4c2; 分区 &#x1f4c2; 分组 &#x1f4c2; inode号 分配…

1113. 红与黑--Flood Fill 算法

目录 1113. 红与黑--Flood Fill 算法---宽搜&#xff08;BFS&#xff09; 输入格式 输出格式 数据范围 输入样例&#xff1a; 输出样例&#xff1a; 思路&#xff1a; 代码&#xff1a; 运行结果&#xff1a; 1113. 红与黑--Flood Fill 算法---宽搜&#xff08;BFS&am…

无人机技术在光伏电站勘探中的重要应用

随着科技的不断进步和创新&#xff0c;无人机技术在各个领域中都发挥着越来越重要的作用。其中&#xff0c;光伏电站对于无人机的应用也成为了行业内的高效运维方式之一&#xff0c;凭借无人机卓越的性能和可靠性&#xff0c;有效的减少了人力运维的危险性和延迟性&#xff0c;…

Java研学-RBAC权限控制(三)

四 部门管理 1 数据库表 CREATE TABLE department (id bigint(20) NOT NULL AUTO_INCREMENT,name varchar(255) DEFAULT NULL,sn varchar(255) DEFAULT NULL,PRIMARY KEY (id) ) ENGINEInnoDB AUTO_INCREMENT9 DEFAULT CHARSETutf8;2 实体类 Data public class Department {…

股票价格预测 | Python股票价格数据导入和处理

文章目录 文章概述代码设计导入处理文章概述 股票价格预测 | Python股票价格数据导入和处理 代码设计 导入 import os import numpy as np import csv import pandas as pd import matplotlib.pyplot

【NC16596】计算系数

题目 计算系数 组合数&#xff0c;快速幂 思路 这是一道数学题&#xff0c;由之前的数学知识可以知道&#xff0c;题目要我们算一个数&#xff1a; ( C k m a n b m ) m o d 10007 (C_k^ma^nb^m)\mod\ 10007 (Ckm​anbm)mod 10007 题意很明显&#xff0c;没有弯弯绕&#xff…

python入门(一)配置环境和选择IDE

Python&#xff0c;作为一种简洁易懂的编程语言&#xff0c;近年来在全球范围内受到了广泛的关注和追捧。它不仅语法简单明了&#xff0c;易于上手&#xff0c;而且拥有强大的第三方库和广泛的应用领域。从数据分析、机器学习到Web开发&#xff0c;Python都能发挥出色的性能&am…

华为OD-C卷-密码解密[100分]

题目描述 给定一段“密文”字符串 s,其中字符都是经过“密码本”映射的,现需要将“密文”解密并输出。 映射的规则(a ~ i)分别用(1 ~ 9)表示;(j ~ z)分别用("10*" ~ "26*")表示。 约束:映射始终唯一。 输入描述 “密文”字符串 输出描述 …

【京东、字节后端二面】讨论不同的系统性能瓶颈点:流量增加时,秒杀系统的性能瓶颈在哪里呢?

1 短网址系统 1.1 如何设计一个短网址系统。什么是这个短网址系统啊&#xff1f; 短网址系统是一种将长网址转换为更短、更易于分享的网址的服务。这类系统特别受欢迎&#xff0c;因为它们可以使复杂的URL变得简单&#xff0c;便于口头传播和在社交媒体上分享&#xff0c;同时…