数据结构初阶(C语言)-二叉树-顺序表建堆

news2024/9/23 5:16:01

一,堆的概念与结构

       如果有⼀个关键码的集合K = {k0 , k1 , k2 , ...,kn−1 },把它的所有元素按完全⼆叉树的顺序存储方式存储,在⼀个⼀维数组中,并满足:,i=0,1,2...则称为小堆(或⼤堆)。将根结点最大的堆叫做最大堆或大根堆,根结点最小的堆叫做最小堆或小根堆。

堆具有以下性质:

1.堆中某个结点的值总是不大于或不小于其父结点的值
2.堆总是⼀棵完全二叉树。

这里我们说一下完全二叉树的性质:

       对于具有 n 个结点的完全二叉树,如果按照从上至下从左至右的数组顺序对所有结点从0 开始编号,则对于序号为 i 的结点有:
1.若 i>0 ,i 位置结点的双亲序号:(i-1)/2 ,i=0,i 为根结点编号,无双亲结点。

2.若 2i+1<n ,左孩子序号: 2i+1 , 2i+1>=n 否则无左孩子。

3.若 2i+2<n ,右孩子序号: 2i+2 , 2i+2>=n 否则无右孩子。

二,堆的实现及

2.1堆的定义 

堆底层结构为数组,因此定义堆的结构为:

typedef int HPDataType;
typedef struct Heap
{
   HPDataType* a;
   int size;
   int capacity;
}HP;

        由于堆在本质上是基于顺序表结构去实现的,所以接下来我们所介绍的实现内容重点介绍与顺序表的不同的地方,相同部分则直接给出代码。

2.2堆的初始化函数HeapInit与堆的空间容量检查函数HpCapacheck

void HeapInit(HP* php)
{
	assert(php);
	php->arr = NULL;
	php->capacity = php->size = 0;
}

void HpCapacheck(HP* hp)
{
	if (hp->capacity == hp->size)
	{
		int exchange = hp->capacity == 0 ? 4 : hp->capacity * 2;
		HeapDataType* tmp = (HeapDataType*)realloc(hp->arr, sizeof(HeapDataType)* exchange);
		if (tmp == NULL)
		{
			perror("realloc:tmp");
			exit(1);
		}
		hp->arr = tmp;
		hp->capacity = exchange;
	}
}

 2.3交换结点数据函数Swap

void Swap(HeapDataType* x, HeapDataType* y)
{
	HeapDataType tmp = *x;
	*x = *y;
	*y = tmp;
}

2.4检查堆结构是否为空函数HeapEmpty

bool HeapEmpty(HP* hp)
{
	assert(hp);
	return (hp->size == 0);
}

2.5向队中插入数据函数HeapPush

void HeapPush(HP* hp, HeapDataType x)
{
	assert(hp);
	HpCapacheck(hp);
	hp->arr[hp->size] = x;
	AdjustUpHp(hp, hp->size);
	hp->size++;
}

     前面的检查空间函数这里我们需要引用,同时判断传过来的指针是否为有效指针,但这时由于我们的堆是一个完全二叉树,对于我们的顺序表插入数据一般是从尾部插入,所以我们这里就需要用到向上调整法来调整我们的新数据使源数据的堆结构不被破坏:

2.5.1向上调整法

void AdjustUpHp(HP* hp, int child)
{
	int parents = (child - 1) / 2;
	while (child > 0)
	{
		if (hp->arr[parents] > hp->arr[child])
		{
			Swap(&hp->arr[parents], &hp->arr[child]);
		}
		else
		{
			break;
		}
		child = parents;
		parents = (child - 1) / 2;
	}
}

        这里我们的向上调整法主要借助了完全二叉树的性质,由于我们新插入的数据是从尾部插入的,那么他的数组对应下标即为size-1,我们是在push之后对size进行的++,所以这里传过去的size即为当前新数据的下标,此时(size-1)/2为当前结点对应的父结点,我们这里建的是小堆,所以于其父结点相比小了便要交换数据,但由于我们先前的堆已经是小堆了,所以只要这里孩子比父大,我们就直接跳出循环,否则继续向上调整。

2.6删除堆中数据函数HeapPop

void HeapPop(HP* hp)
{
	assert(hp && !HeapEmpty(hp));
	Swap(&hp->arr[hp->size - 1], &hp->arr[0]);
	hp->size--;
	AdjustDownHp(hp,0);
}

       对堆的数据删除是从堆的最顶端的数据开始的,所以我们为了最大程度上减轻删除数据后对堆的影响,这里我们可以直接将根结点的数据与堆的最后一个数据交换,这样在尾删就会最小程度的影响堆结构。当然,新的数据到最顶端后,还需要进行调整,恢复堆的结构:

2.6.1向下调整法

void AdjustDownHp(HP* hp, int parents)
{
	int child = parents * 2 + 1;
	while (child < hp->size)
	{
		if (child + 1 < hp->size && hp->arr[child] > hp->arr[child + 1])
		{
			child++;
		}
		if (hp->arr[child] < hp->arr[parents])
		{
			Swap(&hp->arr[child], &hp->arr[parents]);
			parents = child;
			child = parents * 2 + 1;

		}
		else
		{
			break;
		}
	}
}

       由于我们最开始是从父结点(根结点)开始调整的,所以为了确保不越界访问,我们需要先确认当前父结点是否有右孩子,如果有,他与左孩子相比谁更小就让谁与父结点比较。最后如果两个IF条件均不成立,我们则跳出循环,这时我们的堆结构就恢复完毕了。

2.7向上/向下调整法直接建堆

       像上面添加删除的去建堆,有些许麻烦,但如果我们直接给一个现成的数组,并将其整理为堆,或许这样建堆会更简单,这里我们就需要用到我们上面介绍的向上向下建堆法。

2.7.1向下建堆法及其时间复杂度的推导

void adjustdownarr(int* arr, int parents, int size)
{
	int child = parents * 2 + 1;
	while(child < size)
	{
		if (child + 1 < size && arr[child] > arr[child + 1])
		{
			child++;
		}
		if (arr[child] < arr[parents])
		{
			Swap(&arr[child], &arr[parents]);
			parents = child;
			child = parents * 2 + 1;
		}
		else
		{
			break;
		}
	}
}

        先说向下调整建堆,这里我们这里传过去的parents为根结点,而size便是有效数据的总个数,这也是由我们向下调整法的特性而实现的,毕竟对每个结点进行向下调整后,它所附属的子树及其本身的所有数据会被调整,最后最小的值被放入当前树的根结点中。直到调整到最后一个子树时便可以成堆。

这时我们来计算向下建堆的时间复杂度:先分析

 

那么我们需要移动的总步数为:(使用错位相减法计算)

(2)-(1)化简可得:

据二叉树的性质可得:

所以我们最终计算出T (n) = n - log2 (n + 1) ≈ n,即向下建堆法的时间复杂度为O(N)。

2.7.2向上建堆法及其时间复杂度

       这里与向下建堆法的思想一致,但向上建堆是先从各个子树开始进行调整,到最后将所有的子树调整完毕得到成型的堆,向上建堆法的代码和时间复杂度的推导这里就不给出了,读者可以自行推导,最终求出来的时间复杂度为O(n ∗ log2 n)。。

       可见向上建堆法时间复杂度上劣于向下建堆法,由于我们实际上现在的电脑内存基本都很大,所以我们在写代码时,更重要的是时间复杂度而不是以前二者均需了。下篇文章我们介绍链式结构堆的相关内容,去体会下递归的暴力美学。

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

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

相关文章

调度子系统在特定时间执行

时序逻辑调度器设计模式允许您安排Simulink子系统在指定时间执行。以下模型说明了这种设计模式。 时序逻辑调度器图表包含以下逻辑&#xff1a; 时序逻辑调度器的关键行为 时序逻辑调度器图表包含两个状态&#xff0c;它们以不同的速率调度函数调用子系统A1、A2和A3的执行&…

frp反向代理的安装与配置、ftp服务的搭建及应用

1、frp简介 frp 是⼀个开源、简洁易⽤、⾼性能的内⽹穿透和反向代理软件&#xff0c;⽀持 tcp, udp, http, https等 协议。frp 项⽬官⽹是 https://github.com/fatedier/frp 2、frp⼯作原理 服务端运⾏&#xff0c;监听⼀个主端⼝&#xff0c;等待客户端的连接&#xff1b; …

基于内容的音乐推荐网站/基于ssm的音乐推荐系统/基于协同过滤推荐的音乐网站/基于vue的音乐平台

获取源码联系方式请查看文末&#x1f345; 摘 要 随着信息化时代的到来&#xff0c;系统管理都趋向于智能化、系统化&#xff0c;音乐推荐网站也不例外&#xff0c;但目前国内的有些公司仍然都使用人工管理&#xff0c;公司规模越来越大&#xff0c;同时信息量也越来越庞大&…

C++中const关键字的多方面应用

const 的基本作用 const有且只有一种作用&#xff0c;那就是限定被修饰的对象无法被修改&#xff0c;在c中&#xff0c;被const修饰的对象被看作常量&#xff0c;存储在只读存储区(.rodata)。 测试代码 const int a 5;char arr[a];对测试代码进行汇编编译 – gcc -S test.c …

机械学习—零基础学习日志(高数09——函数图形)

零基础为了学人工智能&#xff0c;真的开始复习高数 函数图像&#xff0c;开始新的学习&#xff01; 幂函数 利用函数的性质&#xff0c;以幂函数为例&#xff0c;因为单调性相同&#xff0c;利用图中的2和3公式&#xff0c;求最值问题&#xff0c;可以直接将式子进行简化。这…

Git之repo sync -c与repo sync -dc用法区别四十八)

简介&#xff1a; CSDN博客专家&#xff0c;专注Android/Linux系统&#xff0c;分享多mic语音方案、音视频、编解码等技术&#xff0c;与大家一起成长&#xff01; 优质专栏&#xff1a;Audio工程师进阶系列【原创干货持续更新中……】&#x1f680; 优质专栏&#xff1a;多媒…

blender使用(四)-物体吸附与晶格形变、阵列、曲线、蒙皮修改器

1.缩放注意点 尽量不要在物体模式下缩放&#xff0c;尽量在编辑模式下缩放。 若在物体模式下做过缩放&#xff0c;右键单击物体&#xff0c;选择应用缩放 2.物体吸附及衰减 吸附&#xff1a;一个物体移动到另一个物体表面时&#xff0c;自动吸附上去 衰减&#xff1a;编辑模式下…

Nginx学习-相关概念

Nginx学习-相关概念 主要学习几个概念&#xff1a;Nginx&#xff0c;正向代理、反向代理、负载均衡、动静分离。–2020年05月29日 什么是Nginx Nginx (engine x) 是一个高性能的HTTP和反向代理web服务器&#xff0c;同时也提供了IMAP/POP3/SMTP服务。 特点是占有内存少&…

探索未知,惊喜连连 —— 盲盒一番赏小程序,开启你的幸运之旅!

在这个充满惊喜与期待的时代&#xff0c;每一份未知都藏着无限可能。我们精心打造的“盲盒一番赏”小程序&#xff0c;正是为了满足您对惊喜的渴望&#xff0c;让每一次点击都成为一次心跳加速的旅程。 &#x1f31f; 【沉浸式体验&#xff0c;触手可及】 想象一下&#xff0c…

【源码阅读】Sony的go breaker熔断器源码探究

文章目录 背景源码分析总结 背景 在微服务时代&#xff0c;服务和服务之间调用、跨部门调用都是很常见的事&#xff0c;但这些调用都存在很多不确定因素&#xff0c;如核心服务A依赖的部门B服务挂掉了&#xff0c;那么A本身的功能将会受到直接的影响&#xff0c;而这些都会影响…

vue this.$refs 动态拼接

业务需要&#xff0c;refs是不固定的 <vxe-grid refgridWarehouse v-bind"gridWarehouseOptions" v-if"tableHeight" :height"tableHeight":expand-config"{iconOpen: vxe-icon-square-minus, iconClose: vxe-icon-square-plus}"c…

(Qt) 文件读写基础

文章目录 &#x1f5c2;️前言&#x1f4c4;ref&#x1f4c4;访问标记&#x1f5c3;️enum 标记 &#x1f5c2;️Code&#x1f4c4;demo&#x1f4c4;分点讲解&#x1f5c3;️继承体系&#x1f5c3;️打开/关闭&#x1f5c3;️写&#x1f5c3;️读 &#x1f5c2;️END&#x1f…

2024年,人工智能行业哪些证书权威?

人工智能领域颁发的证书有很多&#xff0c;但哪些更权威呢&#xff1f;看颁发证书的单位&#xff01; 工信部电子标准院的人工智能从业人员认证证书是由工业和信息化部电子工业标准化研究院&#xff08;以下简称“电子标准院”&#xff09;颁发&#xff0c;旨在评价和认证人工…

【嵌入式开发之标准I/O】文件I/O的基本概念,打开、关闭、定位函数及实例

文件I/O和标准I/O 什么是文件I/O?什么是标准I/O? 文件I/O&#xff1a;文件I/O又称系统IO&#xff0c;系统调用&#xff0c;称之为不带缓存的IO&#xff08;unbuffered I/O)。是操作系统提供的API接口函数。不带缓存指的是每个read&#xff0c;write都调用内核中的一个系统调…

【HarmonyOS4学习笔记】《HarmonyOS4+NEXT星河版入门到企业级实战教程》课程学习笔记(二十三)

课程地址&#xff1a; 黑马程序员HarmonyOS4NEXT星河版入门到企业级实战教程&#xff0c;一套精通鸿蒙应用开发 &#xff08;本篇笔记对应课程第 33 节&#xff09; P33《32.通知-进度条通知》 下载按钮对应的逻辑&#xff1a; 取消按钮对应的逻辑&#xff1a; 暂停按钮对应的…

Java | Leetcode Java题解之第274题H指数

题目&#xff1a; 题解&#xff1a; class Solution {public int hIndex(int[] citations) {int left0,rightcitations.length;int mid0,cnt0;while(left<right){// 1 防止死循环mid(leftright1)>>1;cnt0;for(int i0;i<citations.length;i){if(citations[i]>mi…

【Django】在anaconda虚拟环境中创建Django项目

文章目录 进入工作目录创建django项目及进入vscode(打开项目目录)选择解析器 进入工作目录 cd C:\WF\developer\djangodemo创建django项目及进入vscode(打开项目目录) django-admin startproject antproject选择解析器 ctrlshiftP打开命令面板

【杰理蓝牙开发】AC695x VM接口原理与使用(1)

本文主要记录 杰理蓝牙VM接口的使用,包括原理的介绍和API接口的使用。 【杰理蓝牙开发】AC695x VM接口原理与使用(1) 0. 个人简介 && 授权须知1. 系统 flash 区域划分1.1 系统区域划分1.2 在配置文件中配置flash区域2. 系统配置项读写接口2.1 读接口:2.2 写接口:2…

MATLAB实验五:MATLAB数据分析

1. 某线路上不同时间对应的电压如下表所示&#xff1a; 1&#xff09;用 3 次多项式拟合(polyfit)该实验曲线&#xff0c;要求绘制 2 原始采样 点&#xff0c;并在 1~8 范围内&#xff0c;使用时间间隔为 0.2 的数据绘制拟合曲线。 建立一个脚本文件&#xff1a;text5_1.m 如下…

【Hot100】LeetCode—279. 完全平方数

目录 题目1- 思路2- 实现⭐完全平方数——题解思路 3- ACM 实现 题目 原题连接&#xff1a;279. 完全平方数 1- 思路 思路 动规五部曲 2- 实现 ⭐完全平方数——题解思路 class Solution {public int numSquares(int n) {// 1. 定义 dpint[] dp new int[n1];//2. 递推公式…