二叉树顺序结构实现【堆的实现】【详细图解】

news2024/11/15 5:01:23
P. S.:以下代码均在VS2019环境下测试,不代表所有编译器均可通过。
P. S.:测试代码均未展示头文件stdio.h的声明,使用时请自行添加。

  

目录

  • 1、二叉树的顺序结构
  • 2、堆的概念
  • 3、堆的实现
    • 3.1 堆实现的前提
      • 3.1.1 向上调整
      • 3.1.2 向下调整
    • 3.2 堆实现
    • 3.2.1 数据插入
    • 3.2.2 数据删除
  • 4、完整代码展示
    • 4.1 Heap.h
    • 4.2 Heap.c
    • 4.3 Heaptest.c
  • 5、拓展堆排序
  • 6、结语





1、二叉树的顺序结构


  二叉树的顺序结构是指将数据以完全二叉树的逻辑结构进行存储的结构。
  但二叉树是多种多样的,为何在日常应用中很少见到普通的二叉树,且看下图中的普通二叉树与完全二叉树的展示。

在这里插入图片描述
  上图中左侧是一个普通的二叉树,右侧是一个完全二叉树(也可称之为满二叉树),当使用顺序结构进行存储时,可以发现作图的存储存在着大量的空间浪费,而右侧的完全二叉树却没有浪费很多空间。


  故在实现二叉树的顺序结构时所使用的是完全二叉树,而这一种存储方式,也称之为堆( PS. 此处的堆和操作系统虚拟进程地址空间中的堆是两回事,一个是数据结构,一个是操作系统中管理内存的一块区域分段)。




2、堆的概念


  若一组数据按照二叉树的顺序存储方式存储在一维数组中,就将其称作 “堆”
  堆也有大堆和小堆之分:
   大堆 :在二叉树中所有的父结点均比子结点所代表的数值大,称之为大堆。
   小堆 :在二叉树中所有的父结点均比子结点所代表的数值小,称之为小堆。
PS. 此处的大堆小堆是指父结点与子结点的大小关系,并不代表所存储数据在一维数组中一定有序。)


有关一维数组中如何寻找父子结点所在位置及其位置之间的关系,回看:
  链接: 数据结构—二叉树相关概念【详解】【画图演示】


有关树中结点之间的关系及其概念,回看:
  链接: 【树】简要理解树的概念




3、堆的实现


3.1 堆实现的前提


  在日常使用中,所输入数据之间维持大堆或小堆关系的情况较少出现,所以在输入数据时,一般需要使用向下调整或向上调整算法对所输入的数据大堆或小堆化,以便更好地使用。
  故在开始堆的实现前,需要先进行向上调整和向下调整算法的实现,后文命名为AdjustUp,AdjustDown。

3.1.1 向上调整


  向上调整是指每一次输入数据时,从输入数据位置作为子结点,与父结点进行比较,若子结点大于父节点,进行数值上的交换(此时构建的是大堆,反之构建的为小堆),交换后将此时的父结点看作新的子结点,再向上进行交换,直至到达根节点或出现子结点小于父结点为止。

在这里插入图片描述

  • 上图为输入28时所展现的向上调整示意图。

具体代码实现如下所示:

void AdjustUp(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;
	}
}
  • 代码块中所展示的Swap函数是一个简单的交换变量数值的函数。

3.1.2 向下调整


  向下调整的方式和向上调整的方式大致相同,以根作为父结点,与其两个子结点中最大的进行比较,若父结点所代表数值小于最大的子结点,则与之交换数值,而后以交换后的子结点作为新的父节点开启新一轮比较,直至出现父节点大于子结点的情况或到达叶子节点(此种方法所构建的是大堆,将上文中的比较词汇取反即可构建小堆)。

在这里插入图片描述

  • 上图中所展示的是通过向下调整构建小堆的过程。

具体代码如下所示:

void AdjustDown(HPDataType* a, int size, int parent)
{
	int child = parent * 2 + 1;
	while (child < size)
	{
		if (child + 1 < size && a[child + 1] > a[child])//child + 1 < size 是为了避免子结点越界
		{
			++child;
		}
		if (a[child] > a[parent])
		{
			Swap(&a[child], &a[parent]);
			parent = child;
			child = parent * 2 + 1;
		}
		else
		{
			break;
		}
	}
}

3.2 堆实现


  因堆的实践中只有核心代码需要解释,其余部分代码将会在标题 4、完整代码展示 中展示。

3.2.1 数据插入


  
  • 结构体等相关定义展示
typedef int HPDataType;

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

void HPPush(HP* php, HPDataType x);

  因堆顺序结构的定义如上,且其中代表数组的是一个指向数据类型的指针,故我们需要进行数组的空间开辟,故出现了如下代码:

if (php->size == php->capacity)
	{
		int newcapacity = php->capacity == 0 ? 4 : 2 * php->capacity;
		HPDataType* tmp = (HPDataType*)realloc(php->a, sizeof(HPDataType) * newcapacity);
		if (tmp == NULL)
		{
			perror("HPPush:realloc fail");
			return;
		}
		php->a = tmp;
		php->capacity = newcapacity;
	}

  上图代码中我们需要先判断数组中的空间是否足够,即php->size == php->capacity,若判断条件成立,则进入扩容阶段,一般在扩容时我们会进行原有数组空间大小的二倍扩容,这一依据是根据相关研究者的研究所得出的结论,并应用realloc对原数组进行扩容。

  • 完整代码展示
void HPPush(HP* php, HPDataType x)
{
	assert(php);
	if (php->size == php->capacity)
	{
		int newcapacity = php->capacity == 0 ? 4 : 2 * php->capacity;
		HPDataType* tmp = (HPDataType*)realloc(php->a, sizeof(HPDataType) * newcapacity);
		if (tmp == NULL)
		{
			perror("HPPush:realloc fail");
			return;
		}
		php->a = tmp;
		php->capacity = newcapacity;
	}
	php->a[php->size] = x;
	php->size++;

	AdjustUp(php->a, php->size - 1);
}

  上述代码块中再插入数据后进行了向上调整,令所插入的数据形成大堆或小堆,以便后续使用。
  向上调整代码及原理回看3.1.1向上调整


3.2.2 数据删除


  数据删除阶段所删除的数据是根节点的数据,在直接删除根节点的数据后,堆中谁能作为新的根节点就成了不可避免的问题,为此可以直接将最后一位数据与第一位数据进行交换,再从根节点开始向下调整,这样就可以从堆中筛选出第二大或第二小的数据来作为新的根,并避免了不必要的麻烦。
  • 完整代码展示
void HPPop(HP* php)
{
	assert(php);
	assert(php->size > 1);
	
	Swap(&php->a[0], &php->a[php->size - 1]);
	
	php->size--;
	
	AdjustDown(php->a, php->size, 0);
}

  向下调整代码及原理回看3.1.2向下调整




4、完整代码展示

4.1 Heap.h

#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <assert.h>

typedef int HPDataType;

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

void Swap(HPDataType* p1, HPDataType* p2);

void HPInit(HP* php);

void HPDestroy(HP* php);

void AdjustUp(HPDataType* a, int child);

void HPPush(HP* php, HPDataType x);

void AdjustDown(HPDataType* a, int size, int parent);

void HPPop(HP* php);

HPDataType HPTop(HP* php);

bool HPEmpty(HP* php);

int HPSize(HP* php);

4.2 Heap.c

#include "Heap.h"

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

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

void HPDestroy(HP* php)
{
	assert(php);
	free(php->a);
	php->a = NULL;
	php->size = php->capacity = 0;
}

void AdjustUp(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 HPPush(HP* php, HPDataType x)
{
	assert(php);
	if (php->size == php->capacity)
	{
		int newcapacity = php->capacity == 0 ? 4 : 2 * php->capacity;
		HPDataType* tmp = (HPDataType*)realloc(php->a, sizeof(HPDataType) * newcapacity);
		if (tmp == NULL)
		{
			perror("HPPush:realloc fail");
			return;
		}
		php->a = tmp;
		php->capacity = newcapacity;
	}
	php->a[php->size] = x;
	php->size++;

	AdjustUp(php->a, php->size - 1);
}

void AdjustDown(HPDataType* 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 HPPop(HP* php)
{
	assert(php);
	assert(php->size > 1);
	
	Swap(&php->a[0], &php->a[php->size - 1]);
	
	php->size--;
	
	AdjustDown(php->a, php->size, 0);
}

HPDataType HPTop(HP* php)
{
	assert(php);
	assert(php->size > 0);
	return php->a[0];
}

bool HPEmpty(HP* php)
{
	assert(php);
	return php->size == 0;
}

int HPSize(HP* php)
{
	assert(php);
	return php->size;
}

4.3 Heaptest.c

#include "Heap.h"

void test()
{
	HP s;
	HPInit(&s);
	HPPush(&s, 123);
	HPPush(&s, 12);
	HPPush(&s, 1);
	HPPush(&s, 321);
	HPPush(&s, 32);
	HPPush(&s, 3);
	printf("输入数据为:123,12,1,321,32,3\n");
	printf("插入数据后数组内成员展示:");
	for (int i = 0; i < s.size; i++)
	{
		printf("%d ", s.a[i]);
	}
	printf("\n");
	HPPop(&s);
	HPPop(&s);
	printf("删除数据后数组内成员展示:");
	for (int i = 0; i < s.size; i++)
	{
		printf("%d ", s.a[i]);
	}
	printf("\n");
	printf("此时调用判空函数后结果展示:");
	if (HPEmpty(&s))
		printf("yes\n");
	else
		printf("no\n");
	printf("此时数组内成员个数:");
	printf("%d\n", HPSize(&s));
	printf("此时根节点的数据为:");
	printf("%d\n", HPTop(&s));
}

int main()
{
	test();
	return 0;
}




5、拓展堆排序


  堆排序的相关内容可以观看博主以往的博文:

    链接: 图解堆排序【一眼看穿逻辑思路】




6、结语


  十分感谢您观看我的原创文章。
  本文主要用于个人学习和知识分享,学习路漫漫,如有错误,感谢指正。
  如需引用,注明地址。

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

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

相关文章

网络安全行为可控定义以及表现内容简述

在数字化快速发展的今天&#xff0c;网络安全已成为国家和企业不可或缺的防线。据统计&#xff0c;网络攻击事件频发&#xff0c;给全球经济带来了巨大损失。因此&#xff0c;确保网络安全行为可控显得尤为重要。今天我们来聊聊网络安全行为可控定义以及表现内容。 网络安全行为…

音视频开发9 FFmpeg 解复用框架说明,重要API说明

一&#xff0c;播放器框架 二 常用音视频术语 容器&#xff0f;文件&#xff08;Conainer/File&#xff09;&#xff1a; 即特定格式的多媒体文件&#xff0c; 比如mp4、flv、mkv等。 媒体流&#xff08;Stream&#xff09;&#xff1a; 表示时间轴上的一段连续数据&#xff0…

维护课堂纪律的重要性

在课堂上&#xff0c;老师们是否经常遇到这样的情况&#xff1a;孩子们交头接耳&#xff0c;小动作不断&#xff0c;甚至有人偷偷玩手机&#xff1f;这些行为长让老师感到头疼&#xff0c;但作为老师&#xff0c;是否思考过&#xff0c;维护课堂纪律的重要性究竟何在&#xff1…

生信网络学院|05月31日《SOLIDWORKS Manage 产品周期管理》

课程主题&#xff1a;SOLIDWORKS Manage 产品周期管理 课程时间&#xff1a;2024年05月31日 14:00-14:30 主讲人&#xff1a;付舰 生信科技 PLM实施顾问 1、SOLIDWORKS Manage介绍 2、周期流程管理 3、产品项目管理 4、项目会议管理 5、项目问题管理 安装腾讯会议客户端…

java配置文件解析yml/xml/properties文件

XML 以mybatis.xml:获取所有Environment中的数据库并连接session为例 import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.Node; import org.w3c.dom.NodeList; import org.xml.sax.SAXException;import javax.xml.parsers.DocumentBuilder; impo…

第十二周 5.21面向对象的三大特性(封装、继承、多态)(二)

三、多态 1.理解: (1)多态:父类型的引用存储不同子类型的对象 父类类名 引用名 new 子类类名(); 引用 对象 父类型 子类型 …

视频太大怎么压缩变小 视频太大了怎么压缩

视频作为一种多媒体形式&#xff0c;在当今社会的重要性日益凸显&#xff0c;其应用范围广泛&#xff0c;影响力深远。 但是视频文件的大小也在不断增加&#xff0c;这给存储和传输带来了很大的困扰。那么&#xff0c;当视频文件过大时&#xff0c;我们该如何将其压缩变小呢&am…

lubuntu20.04安装和使用ROS Noetic Ninjemys

Noetic Ninjemys 最后一个ROS官方支持的第一代Noetic Ninjemys 为何选择Lubuntu 熟悉我博客的朋友知道&#xff0c;我的这些分享都没有官方经费支持&#xff0c;都是在自己和志同道合朋友们共同努力下&#xff0c;走到今天。 设备陈旧&#xff0c;只能选择对系统资源需求最少…

Common Lisp笔记

在计划学习函数式编程的时候&#xff0c;我一开始打算学习的是 F#。因为我朋友就是在 DTU 上的学&#xff0c;F# 就是 DTU&#xff08;丹麦理工&#xff09;开发的。但是由于 F# 和微软的 .NET 绑定&#xff0c;而在 macOS 上&#xff0c;目前版本的 .NET 的是有些问题的&#…

【NumPy】NumPy线性代数模块详解:掌握numpy.linalg的核心功能

&#x1f9d1; 博主简介&#xff1a;阿里巴巴嵌入式技术专家&#xff0c;深耕嵌入式人工智能领域&#xff0c;具备多年的嵌入式硬件产品研发管理经验。 &#x1f4d2; 博客介绍&#xff1a;分享嵌入式开发领域的相关知识、经验、思考和感悟&#xff0c;欢迎关注。提供嵌入式方向…

软件测试人员如何规划自己的职业发展路线

在这个飞速发展的时代中&#xff0c;我们每个人都渴望在各自的领域里找到属于自己的一片天空&#xff0c;而对于那些致力于软件测试的朋友们而言&#xff0c;规划好自己的职业发展路线显得尤为重要。 一、明确目标 首先&#xff0c;我们需要确立一个明确的职业发展目标。对于软…

C# yolov8 TensorRT Demo

C# yolov8 TensorRT Demo 目录 效果 说明 项目 代码 下载 效果 说明 环境 NVIDIA GeForce RTX 4060 Laptop GPU cuda12.1cudnn 8.8.1TensorRT-8.6.1.6 版本和我不一致的需要重新编译TensorRtExtern.dll&#xff0c;TensorRtExtern源码地址&#xff1a;https://githu…

《爷爷的信》获短片金奖:电影新星的摇篮与短片的春天

随着上汽大众杯澳涞坞全球青年电影短片大赛金奖的公布&#xff0c;我们迎来了短片创作的一个崭新里程碑。这一赛事不仅为青年电影人搭建了一个展示才华的舞台&#xff0c;更预示着短片艺术即将迈入一个繁荣的新纪元。 澳涞坞集团此次大赛的举办&#xff0c;是对青年创作力量的…

美国西储大学(CRWU)轴承故障诊断——连续小波(CWT)变换

1.数据集介绍 2.代码 import random import matplotlib matplotlib.use(Agg) from scipy.io import loadmat import numpy as npdef split(DATA):step = 400;size = 1024;data = []for i in range(1, len(DATA) - size, step):data1 = DATA[i:i + size]data.append(data1)rand…

【NumPy】深入理解NumPy的cov函数:计算协方差矩阵的完整指南

&#x1f9d1; 博主简介&#xff1a;阿里巴巴嵌入式技术专家&#xff0c;深耕嵌入式人工智能领域&#xff0c;具备多年的嵌入式硬件产品研发管理经验。 &#x1f4d2; 博客介绍&#xff1a;分享嵌入式开发领域的相关知识、经验、思考和感悟&#xff0c;欢迎关注。提供嵌入式方向…

全网首发!精选32个最新计算机毕设实战项目(附源码),拿走就用!

Hi 大家好&#xff0c;马上毕业季又要开始了&#xff0c;陆陆续续又要准备毕业设计了&#xff0c;有些学生轻而易举就搞定了&#xff0c;有些学生压根没有思路怎么做&#xff0c;可能是因为技术问题&#xff0c;也可能是因为经验问题。 计算机毕业相关的设计最近几年类型比较多…

基于半花青的近红外荧光材料

一、基于半花青的酯酶响应的光声探针&#xff1a; 参考文献&#xff1a;De Novo Design of Activatable Photoacoustic/Fluorescent Probes for Imaging Acute Lung Injury In Vivo | Analytical Chemistry (acs.org) 1.季铵盐的结构改造&#xff1a; 之前的基于半花青的近红外…

Linux网络_网络基础预备

文章目录 前言一、网络基础知识网络协议协议分层OSI七层模型TCP/IP五层(或四层)模型 认识IP地址认识MAC地址数据包封装和分用 前言 Linux系统编程已经告一段落&#xff0c;但是我们在学习LInux系统编程所积累的知识&#xff0c;将仍然与后面网络知识强相关&#xff0c;学习网络…

Nature plants|做完单细胞还可以做哪些下游验证实验

中国科学院分子植物科学中心与南方科技大学在《Nature Plants》期刊上(IF18.0)发表了关于苜蓿根瘤共生感知和早期反应的文章&#xff0c;该研究首次在单细胞水平解析了结瘤因子处理蒺藜苜蓿&#xff08;Medicago truncatula&#xff09;根系24小时内特异细胞类型的基因表达变化…

c++(四)

c&#xff08;四&#xff09; 运算符重载可重载的运算符不可重载的运算符运算符重载的格式运算符重载的方式友元函数进行运算符重载成员函数进行运算符重载 模板定义的格式函数模板类模板 标准模板库vector向量容器STL中的listmap向量容器 运算符重载 运算符相似&#xff0c;运…