【数据结构入门】二叉树之堆的实现

news2024/11/15 4:21:37

文章目录

  • 前言
  • 一、树
    • 1.1 树的概念
    • 1.2 树的相关概念
  • 二、二叉树
    • 2.1 二叉树的概念
    • 2.2 特殊的二叉树
    • 2.3 二叉树的性质
  • 三、堆
    • 3.1 堆的概念
    • 3.2 堆的性质
    • 3.3 堆的存储
    • 3.4 堆的实现
      • 3.4.1 堆的初始化
      • 3.4.2 堆的销毁
      • 3.4.1 堆向上调整算法
      • 3.4.2 堆向下调整算法
      • 3.4.3 堆的创建
      • 3.4.4 堆的插入
      • 3.4.5 堆的删除
      • 3.4.6 堆顶元素
      • 3.4.7 判断堆是否为空
      • 3.4.8 堆的元素个数
      • 3.4.9 Heap.h
  • 总结

前言

堆是一种重要的数据结构,常用于解决各种问题,如优先队列、排序算法、图算法等。堆具有很多特性,其中最常见的是最大堆和最小堆。最大堆中,每个节点的值都大于等于其子节点的值,而最小堆则相反,每个节点的值都小于等于其子节点的值。
 在本文中,我们将详细介绍堆的概念、性质和操作。我们将以一个具体的例子来说明堆的构建和调整过程,并通过图示展示堆的结构。最后,我们还将讨论堆在实际应用中的一些常见用途和算法。通过学习堆,您将能够更好地理解和应用这一重要的数据结构。
 要想理解堆,我们先需要知道树的概念,事实上堆是一种二叉树。

一、树

1.1 树的概念

树是一种非线性的数据结构,它是由n(n>=0)个有限结点组成一个具有层次关系的集合。把它叫做树是因为它看起来像一棵倒挂的树,也就是说它是根朝上,而叶朝下的

  • 有一个特殊的结点,称为根结点,根结点没有前驱结点
  • 除根结点外,其余结点被分成M(M>0)个互不相交的集合T1、T2、……、Tm,其中每一个集合Ti(1<= i <= m)又是一棵结构与树类似的子树。每棵子树的根结点有且只有一个前驱,可以有0个或多个后继
  • 因此,树是递归定义的。
    在这里插入图片描述
    注意:树形结构中,子树之间不能有交集,否则就不是树形结构
    在这里插入图片描述

1.2 树的相关概念

在这里插入图片描述

结点的度:一个结点含有的子树的个数称为该结点的度; 如上图:A的为6

叶结点或终端结点:度为0的结点称为叶结点; 如上图:B、C、H、I…等结点为叶结点

非终端结点或分支结点:度不为0的结点; 如上图:D、E、F、G…等结点为分支结点

双亲结点或父结点:若一个结点含有子结点,则这个结点称为其子结点的父结点; 如上图:A是B的父结点

孩子结点或子结点:一个结点含有的子树的根结点称为该结点的子结点; 如上图:B是A的孩子结点

兄弟结点:具有相同父结点的结点互称为兄弟结点; 如上图:B、C是兄弟结点(也就是亲兄弟结点)

树的度:一棵树中,最大的结点的度称为树的度; 如上图:树的度为6

结点的层次:从根开始定义起,根为第1层,根的子结点为第2层,以此类推;

树的高度或深度:树中结点的最大层次; 如上图:树的高度为4

堂兄弟结点:双亲在同一层的结点互为堂兄弟;如上图:H、I互为兄弟结点

结点的祖先:从根到该结点所经分支上的所有结点;如上图:A是所有结点的祖先

子孙:以某结点为根的子树中任一结点都称为该结点的子孙。如上图:所有结点都是A的子孙

森林:由m(m>0)棵互不相交的树的集合称为森林

二、二叉树

2.1 二叉树的概念

一棵二叉树是结点的一个有限集合,该集合:

  1. 或者为空
  2. 由一个根结点加上两棵别称为左子树和右子树的二叉树组成
    在这里插入图片描述
    从上图可以看出:

1.二叉树不存在度大于2的结点
2. 二叉树的子树有左右之分,次序不能颠倒,因此二叉树是有序树

注意:对于任意的二叉树都是由以下几种情况复合而成的:
在这里插入图片描述

2.2 特殊的二叉树

  1. 满二叉树:一个二叉树,如果每一个层的结点数都达到最大值,则这个二叉树就是满二叉树。也就是说,如果一个二叉树的层数为K,且结点总数是 2 k − 1 2^k -1 2k1 ,则它就是满二叉树。
  2. 完全二叉树:完全二叉树是效率很高的数据结构,完全二叉树是由满二叉树而引出来的。对于深度为K的,有n个结点的二叉树,当且仅当其每一个结点都与深度为K的满二叉树中编号从1至n的结点一一对应时称之为完全二叉树。 要注意的是满二叉树是一种特殊的完全二叉树。
    在这里插入图片描述

2.3 二叉树的性质

  1. 若规定根结点的层数为1,则一棵非空二叉树的第i层上最多有 2 ( i − 1 ) 2^{(i-1)} 2(i1) 个结点.
  2. 若规定根结点的层数为1,则深度为h的二叉树的最大结点数是 2 h − 1 2^h-1 2h1.
  3. 对任何一棵二叉树, 如果度为0其叶结点个数为 n 0 n_0 n0, 度为2的分支结点个数为 n 2 n_2 n2,则有 n 0 n_0 n0 n 2 n_2 n2+1
  4. 若规定根结点的层数为1,具有n个结点的满二叉树的深度h= l o g 2 ( n + 1 ) log_2(n+1) log2(n+1). (ps: l o g 2 ( n + 1 ) log_2(n+1) log2(n+1)是log以2为底,n+1为对数)
  5. 对于具有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否则无右孩子

三、堆

3.1 堆的概念

如果有一个关键码的集合K = { k 0 k_0 k0 k 1 k_1 k1 k 2 k_2 k2,…, k n − 1 k_{n-1} kn1},把它的所有元素按完全二叉树的顺序存储方式存储在一个一维数组中,并满足: K i K_i Ki <= K 2 ∗ i + 1 K_{2*i+1} K2i+1 K i K_i Ki<= K 2 ∗ i + 2 K_{2*i+2} K2i+2 ( K i K_i Ki >= K 2 ∗ i + 1 K_{2*i+1} K2i+1 K i K_i Ki >= K 2 ∗ i + 2 K_{2*i+2} K2i+2) i = 0,1,2…,则称为小堆(或大堆)。将根结点最大的堆叫做最大堆或大根堆,根结点最小的堆叫做最小堆或小根堆。

3.2 堆的性质

  • 堆中某个结点的值总是不大于或不小于其父结点的值;
  • 堆总是一棵完全二叉树。
    在这里插入图片描述

3.3 堆的存储

堆一般使用顺序结构的数组来存储,但是普通的二叉树是不适合用数组来存储的,因为可能会存在大量的空间浪费。堆作为一个完全二叉树是完全可以用数组来存储的,不会浪费太多空间。

3.4 堆的实现

3.4.1 堆的初始化

  1. assert 判空,防止野指针的出现
  2. 初始容量设为4
void HeapInit(HP* php)
{
	assert(php);

	php->a = (HPDataType*)malloc(sizeof(HPDataType)*4);
	if (php->a == NULL)
	{
		perror("malloc fail");
		return;
	}

	php->size = 0;
	php->capacity = 4;
}

3.4.2 堆的销毁

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

数组的销毁,比较简单

3.4.1 堆向上调整算法

基本思想:

向上调整算法有一个前提:左右子树必须是一个堆,才能调整。所以在建堆时,要建好大堆或者小堆。

具体步骤:

  1. 将新插入的元素放置在堆的最后一个位置(通常是数组的末尾)。
  2. 将该元素与其父节点进行比较。
  3. 若该元素大于(或小于,具体根据堆是最大堆还是最小堆而定)其父节点的值,则交换该元素和其父节点的位置。
  4. 继续向上对比和交换,直到满足堆的性质或达到堆的根节点。
void ADjustUp(HPDataType* a, int child)
{
	int parent = (child - 1) / 2;
	//不建议while(parent>=0) 因为parent到不了-1,但是也可以跑,因为后面if条件不满足
	while (child>0)
	{
		if (a[child] > a[parent])
		{
			Swap(&a[child], &a[parent]);
			child = parent;
			parent = (child - 1) / 2;
		}
		else
		{
			break;
		}
	}
}

3.4.2 堆向下调整算法

向下调整算法与向上调整一样,但是它的时间复杂度比向上调整算法低:左右子树必须是一个堆,才能调整。所以在建堆时,要建好大堆或者小堆。

具体步骤:

  1. 从根结点开始向下调整。
  2. 将该元素与较小或较大的子节点进行比较。
  3. 若该元素大于(或小于,具体根据堆是最大堆还是最小堆而定)其子节点的值,则交换该元素和子节点的位置。
  4. 继续向下对比和交换,直到满足堆的性质或达到堆的叶节点。
void ADjustDown(HPDataType* a, int sz, int parent)
{
	int child = parent * 2 + 1;
	while (child < sz)
	{
		//选出左右孩子中大的一个
		//这里child+1的判断在前,不要先访问再判断
		//这里a[child + 1] > a[child] 建大堆用>, 建小堆用<
		if (child + 1 < sz && a[child + 1] < a[child])
		{
			//这地方可能会越界
			++child;
		}
		//这里a[child] > a[parent] 建大堆用>, 建小堆用<
		if (a[child] < a[parent])
		{
			Swap(&a[child], &a[parent]);
			parent = child;
			child = parent * 2 + 1;
		}
		else
		{
			break;
		}
	}
}

3.4.3 堆的创建

向下调整建堆

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

void HeapInitArray(HP* php, int* a, int n)
{
	assert(php);

	php->a = (HPDataType*)malloc(sizeof(HPDataType) * 4);
	if (php->a == NULL)
	{
		perror("malloc fail");
		return;
	}

	php->size = 0;
	php->capacity = n;

	//建堆
	for (int i = (n - 1 - 1) / 2; i >= 0; i--)
	{
		ADjustDown(a, n, i);
	}
	int end = n - 1;
	while (end > 0)
	{
		Swap(&a[end], &a[0]);
		ADjustDown(a, end, 0);
		--end;
	}
}

3.4.4 堆的插入

判断容量不够时需要扩容,空间够,则插入后需要向上调整

void HeapPush(HP* php, HPDataType x)
{
	assert(php);

	if (php->size == php->capacity)
	{
		HPDataType* tmp = (HPDataType*)realloc(php->a, sizeof(HPDataType) * php->capacity * 2);
		if (tmp == NULL)
		{
			perror("realloc fail");
			return;
		}

		php->a = tmp;
		php->capacity *= 2;
	}
	php->a[php->size] = x;
	php->size++;

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

3.4.5 堆的删除

堆的删除要从堆顶开始删除,删除时先将堆顶元素与堆底元素进行交换,然后去掉堆底元素(也就是删掉了堆顶元素),然后再进行向下调整,保证堆的结构依旧满足。
这里从堆顶开始删除才有意义,堆顶的数据依次会是最大值(大堆)或者最小值(小堆)。

void HeapPop(HP* php)
{
	assert(php);
	assert(!HeapEmpty(php));

	Swap(&php->a[0], &php->a[php->size-1]);
	php->size--;

	ADjustDown(php->a, php->size, 0);
}

3.4.6 堆顶元素

HPDataType HeapTop(HP* php)
{
	assert(php);

	return php->a[0];
}

3.4.7 判断堆是否为空

bool HeapEmpty(HP* php)
{
	assert(php);

	return php->size == 0;
}

3.4.8 堆的元素个数

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

3.4.9 Heap.h

#pragma once

typedef int HPDataType;

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

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


void HeapInit(HP* php);
void HeapDestory(HP* php);
void HeapInitArray(HP* php, int* a, int n);


void HeapPush(HP* php, HPDataType x);
void HeapPop(HP* php);
HPDataType HeapTop(HP* php);
bool HeapEmpty(HP* php);
int HeapSize(HP* php);
void Swap(HPDataType* p1, HPDataType* p2);

void ADjustUp(HPDataType* a, int child);
void ADjustDown(HPDataType* a, int sz, int parent);

总结

堆的主要优点之一是能够在O(log n)的时间复杂度内找到最值,例如最大堆可以在常数时间内找到最大值。这使得堆可以在动态数据集合中高效地插入和删除元素。另外,堆还能够在排序算法中扮演重要角色,例如堆排序可以在O(n log n)的时间复杂度内对数据进行排序。

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

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

相关文章

MT2523AS 原边10瓦无外围方案PCB设计要点

MT2523AS 是自供电原边反馈5V2A(10瓦)电源芯片。MT2523AS 内置功率三极管&#xff0c;采用脉冲频率调制&#xff08;PFM&#xff09;建立非连续导电模式&#xff08;DCM&#xff09;的反激式电源&#xff0c;外围设计极简化。MT2523AS 具有可变原边峰值电流&#xff0c;通过最大…

AI技术在招聘人才笔试测评中的作用

一、引言 在快速变化的商业环境中&#xff0c;企业之间的竞争日益激烈&#xff0c;而人才作为企业发展的核心驱动力&#xff0c;其选拔与培养显得尤为重要。传统的人才招聘流程&#xff0c;尤其是笔试测评环节&#xff0c;往往依赖于人工阅卷、主观判断&#xff0c;不仅效率低…

每日一问:深入理解JVM——结构与类的加载过程解析

每日一问&#xff1a;深入理解JVM——结构与类的加载过程解析 在Java的世界中&#xff0c;JVM&#xff08;Java Virtual Machine&#xff0c;Java虚拟机&#xff09;是一个核心概念。它是Java程序能够跨平台运行的基础&#xff0c;负责执行Java字节码&#xff0c;并为Java应用程…

成为Python砖家(3): 何时产生字节码 .pyc 文件

好奇&#xff1a;.pyc和 __pycache__是啥&#xff1f; 你是否好奇&#xff0c;在某些 Python 工程中&#xff0c;当执行了 xxx.py脚本后&#xff0c;多出了 __pycache__目录&#xff1f;这个目录下存放的是一些 .pyc结尾的文件。 这些文件&#xff0c;叫做 python bytecode。 …

电子信息工程专业学习路线的制定与实践

电子信息工程专业是一个多学科交叉、技术更新迅速的领域。对于大学生来说&#xff0c;制定合适的学习路线并有效学习专业知识至关重要。 目录 一、明确学习目标 二、构建知识体系 三、掌握基础知识 四、深入专业课程 五、实践与理论相结合 六、学习编程语言 七、参与科研…

345345

c语言中的小小白-CSDN博客c语言中的小小白关注算法,c,c语言,贪心算法,链表,mysql,动态规划,后端,线性回归,数据结构,排序算法领域.https://blog.csdn.net/bhbcdxb123?spm1001.2014.3001.5343 给大家分享一句我很喜欢我话&#xff1a; 知不足而奋进&#xff0c;望远山而前行&am…

数学建模起步感受(赛前15天)

0基础直接上手数模&#xff0c;因为大一&#xff01;年轻就是无所畏惧&#xff01;开个玩笑&#xff0c;因为数模比赛比一年少一年… 抱着不打也是浪费的态度&#xff0c;我开始着手准备 首先python啥也不会&#xff0c;知道有元组这玩意… 仅仅在刷软考题的时候遇到python选择…

[数据集][目标检测]竹子甘蔗发芽缺陷检测数据集VOC+YOLO格式2953张3类别

数据集格式&#xff1a;Pascal VOC格式YOLO格式(不包含分割路径的txt文件&#xff0c;仅仅包含jpg图片以及对应的VOC格式xml文件和yolo格式txt文件) 图片数量(jpg文件个数)&#xff1a;2953 标注数量(xml文件个数)&#xff1a;2953 标注数量(txt文件个数)&#xff1a;2953 标注…

计算机Java项目|基于SpringBoot的医疗报销系统的设计与实现

作者主页&#xff1a;编程指南针 作者简介&#xff1a;Java领域优质创作者、CSDN博客专家 、CSDN内容合伙人、掘金特邀作者、阿里云博客专家、51CTO特邀作者、多年架构师设计经验、多年校企合作经验&#xff0c;被多个学校常年聘为校外企业导师&#xff0c;指导学生毕业设计并参…

【深入浅出Docker】【三】Docker容器详解

文章目录 一. Docker容器简介二. Docker容器详解1. 容器vs虚拟机1.1. 虚拟机模型1.2. 容器模型1.3. 虚拟机的额外开销 2. 容器启动过程描述3. 容器进程4. 容器生命周期与文件保存5. 优雅地停止容器&#xff1a;两阶段方式停止并删除容器6. 利用重启策略进行容器的自我修复6.1. …

MobileFormer 网络简介

MobileFormer&#xff1a;一种通过双线桥将 MobileNet 和 Transformer 并行的结构。这种方式融合了 MobileNet 局部性表达能力和 Transformer 全局表达能力的优点&#xff0c;这个桥能将局部性和全局性双向融合。和现有 Transformer 不同&#xff0c;Mobile-Former 使用很少的 …

或许改变整个领域的生态!颜宁团队合作最新Cell子刊

电压门控钠(Nav)和钙(Cav)通道负责电信号的起始。长期以来&#xff0c;它们一直是治疗各种疾病的靶标。来自多种生物的Nav和Cav通道的不同亚型的冷冻电镜(cryo-EM)结构越来越多&#xff0c;需要一个通用的残基编号系统来建立结构-功能关系&#xff0c;并有助于合理的药物设计或…

java(基础)

Arrays.toString 依赖于 java.util.* Pearson出版社 Java优势 1 ) 简单性 2 ) 面向对象 3 ) 分布式 4 ) 健壮性 5 ) 安全性 7 ) 可移植性 可移植性指的是 Java 程序可以在不同的操作系统、硬件平台和设备上运行 8 ) 解释型 9 ) 高性能 10 ) 多线程 11 ) 动态性 6 )…

打字侠支持新世纪五笔、86版五笔、98版五笔打字练习:初学者的最佳选择

在当今数字化时代&#xff0c;打字已经成为我们日常生活和工作中必不可少的一部分。尽管拼音输入法因其易学易用的特点占据了主导地位&#xff0c;但对于那些追求高效打字和提高汉字输入速度的人来说&#xff0c;五笔输入法仍然是一种极具吸引力的选择。无论是新世纪五笔、86版…

C++的发展史及前景

&#x1f308;个人主页&#xff1a;Yui_ &#x1f308;Linux专栏&#xff1a;Linux &#x1f308;C语言笔记专栏&#xff1a;C语言笔记 &#x1f308;数据结构专栏&#xff1a;数据结构 &#x1f308;C专栏&#xff1a;C 文章目录 1. 什么是C2. C的发展史3. C的重要性3.1 C的使…

Linux日常运维-任务计划(crontab)

作者介绍&#xff1a;简历上没有一个精通的运维工程师。希望大家多多关注作者&#xff0c;下面的思维导图也是预计更新的内容和当前进度(不定时更新)。 本小章内容就是Linux进阶部分的日常运维部分&#xff0c;掌握这些日常运维技巧或者方法在我们的日常运维过程中会带来很多方…

2024年软件测试经典面试题(全三篇)【包含答案】做完面试进入大厂不是梦

前言 迎来的便是金九银十&#xff0c;一直想着说分享一些软件测试的面试题&#xff0c;这段时间做了一些收集和整理&#xff0c;下面共有三篇经典面试题&#xff0c;大家可以试着做一下&#xff0c;答案附在后面&#xff0c;希望能帮助到大家。 软件测试经典面试题&#xff0…

HTTP协议详细图解(请求报文格式,响应报文格式,get和post的区别,状态码详解)

文章目录 什么是Http协议&#xff1f;HTTP报文格式HTTP 请求格式HTTP响应格式什么是 URL请求和响应中的“方法”GET 和 POST 的区别认识“Header”状态码详解 什么是Http协议&#xff1f; Http协议是“超文本传输协议”&#xff08;不仅可以传输文本&#xff0c;也可以传输图片…

六大桌面管理系统一次性打包分享,寻找最好的桌面管理系统

桌面乱了需要好好整理&#xff0c;不仅看着干净还能迅速找到想要的东西。对于电脑也一样&#xff0c;尤其是员工的电脑桌面&#xff0c;如果不监控&#xff0c;你不知道员工会在电脑上进行什么神奇的操作&#xff0c;也许删除了一个重要文件&#xff0c;也许下载了一个病毒软件…

VBA技术资料MF186:读取文件属性及自定义属性

我给VBA的定义&#xff1a;VBA是个人小型自动化处理的有效工具。利用好了&#xff0c;可以大大提高自己的工作效率&#xff0c;而且可以提高数据的准确度。“VBA语言専攻”提供的教程一共九套&#xff0c;分为初级、中级、高级三大部分&#xff0c;教程是对VBA的系统讲解&#…