数据结构:8、堆

news2024/11/16 12:43:52

一、树的概念

树是一种非线性的数据结构,它是由n(n>=0)个有限结点组成一个具有层次关系的集合。把它叫做树是因为它看起来像一棵倒挂的树,也就是说它是根朝上,而叶朝下的。
有一个特殊的结点,称为根结点,根节点没有前驱结点除根节点外,其余结点被分成M(M>0)个互不相交的集合T1、T2、……、Tm,其中每一个集合Ti(1<= i<= m)又是一棵结构与树类似的子树。每棵子树的根结点有且只有一个前驱,可以有0个或多个后继因此,树是递归定义的。

节点的度:一个节点含有的子树的个数称为该节点的度; 如上图: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)棵互不相交的树的集合称为森林;

树的应用最简单的就是文件夹,例如我们打开一个文件是先选入c盘这种,这个c盘就相当于树的祖先,而C盘下面的文件就是相当于他的子节点,这个就是最简单的应用。

而书如果要用c语言去写就是一个结构体,但是一般有几种写法如:双亲表示法,孩子表示法、孩子双亲表示法以及孩子兄弟表示法等,但是最常用的就是左孩子右兄弟表示法,也就是孩子兄弟表示法,如下图,根节点中存着左边的第一个节点,和右边的兄弟节点,兄弟节点直到空,这样就能全部遍历了。

二、二叉树的概念

一棵二叉树是结点的一个有限集合,该集合:
1. 或者为空
2. 由一个根节点加上两棵别称为左子树和右子树的二叉树组成

从上图可以看出:
1. 二叉树不存在度大于2的结点
2. 二叉树的子树有左右之分,次序不能颠倒,因此二叉树是有序树


注意:对于任意的二叉树都是由以下几种情况复合而成的。

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

二叉树的性质
1. 若规定根节点的层数为1,则一棵非空二叉树的第i层上最多有2^(i-1)个结点
2. 若规定根节点的层数为1,则深度为h的二叉树的最大结点数是2^h-1
3. 对任何一棵二叉树, 如果度为0其叶结点个数为n0 , 度为2的分支结点个数为n2 ,则有 n0=n2 +1
4. 若规定根节点的层数为1,具有n个结点的满二叉树的深度,h=log2(n+1)(ps:log2(n+1)是log以2为底,n+1为对数)
5. 对于具有n个结点的完全二叉树,如果按照从上至下从左至右的数组顺序对所有节点从0开始编号,则对于序号为i的结点有:
①若i>0,i位置节点的双亲序号:(i-1)/2;i=0,i为根节点编号,无双亲节点
②若2i+1<n,左孩子序号:2i+1,2i+1>=n否则无左孩子
③若2i+2<n,右孩子序号:2i+2,2i+2>=n否则无右孩子

 二叉树的存储结构
二叉树一般可以使用两种结构存储,一种顺序结构,一种链式结构。
1. 顺序存储
顺序结构存储就是使用数组来存储,一般使用数组只适合表示完全二叉树,因为不是完全二叉树会有空间的浪费。而现实中使用中只有堆才会使用数组来存储,关于堆我们后面的章节会专门讲解。二叉树顺序存储在物理上是一个数组,在逻辑上是一颗二叉树。

2. 链式存储
二叉树的链式存储结构是指,用链表来表示一棵二叉树,即用链来指示元素的逻辑关系。 通常的方法是链表中每个结点由三个域组成,数据域和左右指针域,左右指针分别用来给出该结点左孩子和右孩子所在的链结点的存储地址 

三、堆的概念

如果有一个关键码的集合K = { k0,k1 ,k2 ,…,k(n-1)},把它的所有元素按完全二叉树的顺序存储方式存储在一个一维数组中,并满足: Ki <=K(2*i+1)且Ki<=K(2*i+2)

( Ki <=K(2*i+1)Ki<=K(2*i+2)) i = 0,1,2…,则称为小堆(或大堆)。将根节点最大的堆叫做最大堆或大根堆,根节点最小的堆叫做最小堆或小根堆。
堆的性质:
堆中某个节点的值总是不大于或不小于其父节点的值;
堆总是一棵完全二叉树。

四、堆的实现

1、堆的创建和销毁

堆的创建这里就是是利用数组,创建一个结构体利用初始化,直接把数组指针置空,然后数组的容量和大小为0,销毁就是free和置空赋值为0,代码如下

typedef int HPDataType;
typedef struct Heap
{
	HPDataType* a;
	int size;
	int capacity;
}Heap;
// 堆的构建
void HeapInit(Heap* hp)
{
	assert(hp);
	hp->a = NULL;
	hp->capacity = 0;
	hp->size = 0;
}

// 堆的销毁
void HeapDestory(Heap* hp)
{
	assert(hp);
	assert(!HeapEmpty(hp));
	hp->capacity = hp->size = 0;
	free(hp->a);
	hp->a = NULL;
}

2、堆的向上调整与向下调整

这个原理就是判断交换,然后这个是那两个数据判断呢?

如图:就是根据图上两个式子去判定数据在哪,代码实现如图。

//向下调整
void AdjustDown(HPDataType* a, int n,int parent)
{
	int chaild = parent * 2 + 1;
	while (chaild<n)
	{
		if (chaild + 1 < n && a[chaild+1] < a[chaild])
		{
			++chaild;
		}
		if (a[chaild] < a[parent])
		{
			Swap(&a[parent], &a[chaild]);
			parent = chaild;
			chaild = parent * 2 + 1;
		}
		else
		{
			break;
		}
	}
}

//向上调整
void AdjustUp(HPDataType* a, int chaild)
{
	assert(a);
	int parent = (chaild - 1) / 2;
	while (chaild > 0)
	{
		if (a[parent] > a[chaild])
		{
			Swap(&a[parent], &a[chaild]);
			chaild = parent;
			parent = (chaild - 1) / 2;
		}
		else
		{
			break;
		}
	}
}

3、堆的插入与删除

插入就是在数组里面插入,然后把计数的size++,就相当于在数组的末尾加数据,相当于数组的存储数据,而size就是记录下标,前面是判断容量大小是否够,然后创建空间。

删除就是把堆顶和堆低的数据交换,然后删除向下调整,如果不交换直接删除,就会让堆里面的关系错乱,代码如图。

// 堆的插入
void HeapPush(Heap* hp, HPDataType x)
{
	assert(hp);
	if (hp->capacity == hp->size)
	{
		int newcapacity = hp->capacity == 0 ? 4 : hp->capacity * 2;
		HPDataType* newnode = (HPDataType*)realloc(hp->a,sizeof(HPDataType) * newcapacity);
		if (newnode == NULL)
		{
			perror("realloc fail");
			return;
		}
		hp->capacity = newcapacity;
		hp->a = newnode;
	}
	
	hp->a[hp->size] = x;
	hp->size++;
	AdjustUp(hp->a, hp->size-1);
	
}

// 堆的删除
void HeapPop(Heap* hp)
{
	assert(hp);
	assert(!HeapEmpty(hp));
	Swap(&hp->a[0], &hp->a[hp->size-1]);
	hp->size--;
	AdjustDown(hp->a, hp->size , 0);
}

4、堆的获取顶部数据

这个直接把数组下标为0的数据返回就行。

// 取堆顶的数据
HPDataType HeapTop(Heap* hp)
{
	assert(hp);
	assert(!HeapEmpty(hp));
	return hp->a[0];
}

5、堆的数据个数

直接返回结构体里面的size就行。

// 堆的数据个数
int HeapSize(Heap* hp)
{
	assert(hp);
	assert(!HeapEmpty(hp));
	return hp->size;
}

6、堆的判空与交换

判空就是当size为0时就返回真,交换就是利用指针去交换数据。

// 堆的判空
bool HeapEmpty(Heap* hp)
{
	assert(hp);
	return hp->size == 0;
}
//交换
void Swap(int* p1, int* p2)
{
	int tmp = *p1;
	*p1 = *p2;
	*p2 = tmp;
}

7、测试以及测试结果

如图1就是把数组里面的数据建堆的形成的数据,图二就是删除顶部数据的后的数据,然后向下调整成小堆,图三就是数据个数和顶部数据的测试。

void TestHeap()
{
	Heap hp;
	HeapInit(&hp);
	int arr[] = { 65,100,70,32,50,60 };
	for (int i = 0; i < sizeof(arr)/sizeof(arr[0]); i++)
	{
		HeapPush(&hp, arr[i]);
	}
	HeapPop(&hp);
	int tmp = HeapTop(&hp);
	int size = HeapSize(&hp);
	HeapDestory(&hp);
}

五、代码

test.c

#define _CRT_SECURE_NO_WARNINGS 1
#include "HP.h"

void TestHeap()
{
	Heap hp;
	HeapInit(&hp);
	int arr[] = { 65,100,70,32,50,60 };
	for (int i = 0; i < sizeof(arr)/sizeof(arr[0]); i++)
	{
		HeapPush(&hp, arr[i]);
	}
	HeapPop(&hp);
	int tmp = HeapTop(&hp);
	int size = HeapSize(&hp);
	HeapDestory(&hp);
}

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

HP.c

#define _CRT_SECURE_NO_WARNINGS 1
#include "HP.h"

// 堆的构建
void HeapInit(Heap* hp)
{
	assert(hp);
	hp->a = NULL;
	hp->capacity = 0;
	hp->size = 0;
}

// 堆的销毁
void HeapDestory(Heap* hp)
{
	assert(hp);
	assert(!HeapEmpty(hp));
	hp->capacity = hp->size = 0;
	free(hp->a);
	hp->a = NULL;
}

// 堆的插入
void HeapPush(Heap* hp, HPDataType x)
{
	assert(hp);
	if (hp->capacity == hp->size)
	{
		int newcapacity = hp->capacity == 0 ? 4 : hp->capacity * 2;
		HPDataType* newnode = (HPDataType*)realloc(hp->a,sizeof(HPDataType) * newcapacity);
		if (newnode == NULL)
		{
			perror("realloc fail");
			return;
		}
		hp->capacity = newcapacity;
		hp->a = newnode;
	}
	
	hp->a[hp->size] = x;
	hp->size++;
	AdjustUp(hp->a, hp->size-1);
	
}

// 堆的删除
void HeapPop(Heap* hp)
{
	assert(hp);
	assert(!HeapEmpty(hp));
	Swap(&hp->a[0], &hp->a[hp->size-1]);
	hp->size--;
	AdjustDown(hp->a, hp->size , 0);
}

// 取堆顶的数据
HPDataType HeapTop(Heap* hp)
{
	assert(hp);
	assert(!HeapEmpty(hp));
	return hp->a[0];
}

// 堆的数据个数
int HeapSize(Heap* hp)
{
	assert(hp);
	assert(!HeapEmpty(hp));
	return hp->size;
}

// 堆的判空
bool HeapEmpty(Heap* hp)
{
	assert(hp);
	return hp->size == 0;
}

//向下调整
void AdjustDown(HPDataType* a, int n,int parent)
{
	int chaild = parent * 2 + 1;
	while (chaild<n)
	{
		if (chaild + 1 < n && a[chaild+1] < a[chaild])
		{
			++chaild;
		}
		if (a[chaild] < a[parent])
		{
			Swap(&a[parent], &a[chaild]);
			parent = chaild;
			chaild = parent * 2 + 1;
		}
		else
		{
			break;
		}
	}
}

//向上调整
void AdjustUp(HPDataType* a, int chaild)
{
	assert(a);
	int parent = (chaild - 1) / 2;
	while (chaild > 0)
	{
		if (a[parent] > a[chaild])
		{
			Swap(&a[parent], &a[chaild]);
			chaild = parent;
			parent = (chaild - 1) / 2;
		}
		else
		{
			break;
		}
	}
}

//交换
void Swap(int* p1, int* p2)
{
	int tmp = *p1;
	*p1 = *p2;
	*p2 = tmp;
}

HP.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 HeapInit(Heap* hp);
// 堆的销毁
void HeapDestory(Heap* hp);
// 堆的插入
void HeapPush(Heap* hp, HPDataType x);
// 堆的删除
void HeapPop(Heap* hp);
// 取堆顶的数据
HPDataType HeapTop(Heap* hp);
// 堆的数据个数
int HeapSize(Heap* hp);
// 堆的判空
bool HeapEmpty(Heap* hp);
//向下调整
void AdjustDown(HPDataType* a, int chaild);
//向上调整
void AdjustUp(HPDataType* a, int chaild);
//交换
void Swap(int* p1, int* p2);

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

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

相关文章

Qt+FFmpeg+opengl从零制作视频播放器-13.打包为exe包发布软件

1.首先visual studio给生成程序添加桌面图标。 右键工程,添加新文件资源文件Resource.rc 选择导入文件,我这里导入了Player.ico文件。 添加后,在资源文件那里就可以看见ico文件。 然后编译release程序, 生成的可执行程序就带上了图标。 2.使用Qt 程序打包发布-windeployq…

用户视角的比特币和以太坊外围技术整理

1. 引言 要点&#xff1a; 比特币L2基本强调交易内容的隐蔽性&#xff0c;P2P交易&#xff08;尤其是支付&#xff09;成为主流&#xff0c;给用户带来一定负担&#xff08;闪电网络&#xff09;在以太坊 L2 中&#xff0c;一定程度上减少了交易的隐蔽性&#xff0c;主流是实…

Linux网络配置修改hosts映射文件关闭防火墙

Linux网络配置&系统管理 一、物理机、VMware软件、虚拟机之间的网络关系1.1 总体框架图1.2 为什么物理机、VM软件、客户机之间能够通信?1.3 查看客户机的IP地址ifconfig1.4 小节1.5 修改静态IP地址1.6 测试能不能ping通 二、修改主机名以及hosts映射文件2.1 修改主机名2.1…

物联网终端telegraf采集设备信息

背景 低功耗设备上资源有限&#xff0c;但又比较重要。对其的管理难度很大&#xff0c;有些时候又必须时刻了解其运行状况。我们自然想到的是能否有办法监控它呢&#xff1f;当时是有的&#xff01;而且很成熟的解决方案。TICK技术栈&#xff0c;那TICK是什么呢&#xff1f; TI…

环保企业应适应行业发展趋势,不断创新和提升竞争力|中联环保圈

《2023年行业评述及2024年发展展望》一文&#xff0c;由中国环保产业协会撰写&#xff0c;全面审视了过去一年我国生态环保产业的发展状况&#xff0c;并对新的一年发展趋势进行了深度预测。该报告以行业主要政策标准为基础&#xff0c;结合报告以及新冠疫情防控转段后的经济恢…

海豚调度系列之:任务类型——SQL节点

海豚调度系列之&#xff1a;任务类型——SQL节点 一、SQL节点二、创建任务三、任务参数四、任务样例五、运行该任务成功之后在hive中查询结果六、使用前置sql和后置sql示例 一、SQL节点 SQL任务类型&#xff0c;用于连接数据库并执行相应SQL。 二、创建任务 点击项目管理-项…

SpringBoot配置达梦数据库依赖(达梦8)

maven配置 <!-- 达梦数据库 --><dependency><groupId>com.dameng</groupId><artifactId>DmJdbcDriver18</artifactId><version>8.1.1.193</version></dependency><dependency><groupId>com.alibaba&l…

已解决:android SDK安装时点击SDK Manager出现闪退

1、首先确保电脑里边安装了JDK&#xff0c;并且要把安装路径配置在环境变量里边&#xff0c;避免使用绝对路径 推荐%JAVA_HOME%\bin 2、在C:\Users\huanhuan\Desktop\android-sdk-windows\tools路径下找到android.bat文件打开&#xff0c;把set java_exe后改为jdk中java.exe的路…

关于stm32(CubeMX+HAL库)的掉电检测以及flash读写

1.掉电检测 CubeMX配置 只需使能PVD中断即可 但是使能了PVD中断后还需要自行配置一些PWR寄存器中的参数&#xff0c;我也通过HAL库进行编写 void PVD_config(void) {//配置PWRPWR_PVDTypeDef sConfigPVD; sConfigPVD.PVDLevel PWR_PVDLEVEL_7; …

元宇宙崛起:区块链与金融科技共绘数字新世界

文章目录 一、引言二、元宇宙与区块链的深度融合三、区块链在元宇宙金融中的应用四、金融科技在元宇宙中的创新应用五、面临的挑战与机遇《区块链与金融科技》亮点内容简介获取方式 一、引言 随着科技的飞速发展&#xff0c;元宇宙概念逐渐走进人们的视野&#xff0c;成为数字…

HTTP中的GET,POST,PUT,DELETE请求方式的区别

前言 Http定义了与服务器交互的不同方法&#xff0c;最基本的方法有4种&#xff0c;分别是GET&#xff0c;POST&#xff0c;PUT&#xff0c;DELETE。 URL全称是统一资源定位符&#xff0c;我们可以这样认为&#xff1a;一个URL地址&#xff0c;它用于描述一个网络上的资源&am…

Unity3d版白银城地图

将老外之前拼接的Unity3d版白银城地图&#xff0c;导入到国内某手游里&#xff0c;改成它的客户端地图模式&#xff0c;可以体验一把手游的快乐。 人物角色用的是它原版的手游默认的&#xff0c;城内显示效果很好&#xff0c;大家可以仔细看看。 由于前期在导入时遇到重大挫折&…

2024腾讯云轻量主机地域怎么选择?上海/北京/广州哪个地域好?

腾讯云轻量应用服务器地域如何选择&#xff1f;地域就近选择&#xff0c;北方选北京地域、南方选广州地域&#xff0c;华东地区选上海地域。广州上海北京地域有什么区别&#xff1f;哪个好&#xff1f;区别就是城市地理位置不同&#xff0c;其他的差不多&#xff0c;不区分好坏…

北大核心期刊《思想政治课教学》投稿有什么要求?

北核《思想政治课教学》收职教、中专、大学、中小学&#xff0c;职称没要求&#xff0c;学生可以 探寻智慧之源&#xff0c;倾听思想之声&#xff0c;欢迎投稿《思想政治课教学》。我们致力于搭建一个开放、前沿的学术交流平台&#xff0c;汇聚国内外思想政治课教学的最新研究成…

如何在Linux Archcraft中配置SSH服务并结合内网穿透实现远程连接

文章目录 1. 本地SSH连接测试2. Archcraft安装Cpolar3. 配置 SSH公网地址4. 公网远程SSH连接小结 5. 固定SSH公网地址6. SSH固定地址连接 Archcraft是一个基于Arch Linux的Linux发行版&#xff0c;它使用最简主义的窗口管理器而不是功能齐全的桌面环境来提供图形化用户界面。 C…

当_WIN32_WINNT大于0x500时,ToolTip窗口不显示问题排查

目录 1、前言 2、回退代码后&#xff0c;ToolTip窗口不显示了 3、使用历史版本比对法找到ToolTip窗口何时开始不显示的 4、为了给字体设置ClearType属性&#xff0c;_WIN32_WINNT宏的值从0x500修改成0x501 5、将_WIN32_WINNT宏值由从0x500修改成0x501&#xff0c;导致系统…

2024最新轻量应用服务器简介_轻量应用服务器购买指南

腾讯云轻量应用服务器开箱即用、运维简单的轻量级云服务器&#xff0c;CPU内存带宽配置高并且价格特别便宜&#xff0c;大带宽&#xff0c;但是限制月流量&#xff0c;轻量2核2G3M带宽61元一年、2核2G4M优惠价99元一年&#xff0c;540元三年、2核4G5M带宽165元一年&#xff0c;…

c++11语法特性

c11 1.c11发展简介 ​ 第一个比较正式的c标准是1998提出的c98标准。之后定了5年计划&#xff0c;每5年来一次大更新。在2003年C标准委员会曾经提交了一份技术勘误表(简称TC1)&#xff0c;使得C03这个名字已经取代了C98称为C11之前的最新C标准名称。不过由于C03(TC1)主要是对C…

java组合模式揭秘:如何构建可扩展的树形结构

组合模式&#xff08;Composite Pattern&#xff09;是一种结构型设计模式&#xff0c;它允许将对象组合成树形结构以表示整体/部分层次结构。组合模式使得客户端可以统一对待单个对象和组合对象&#xff0c;从而使得客户端可以处理更复杂的结构。 组合模式的主要组成部分包括&…

Text-to-SQL 工具Vanna进阶|数据库对话机器人的多轮对话

跟数据库对话机器人对话,我可不止一个问题。 可能基于第一句问话,还有第二句、第三句问话。。。第N句对话。所以本文测试了多轮对话功能。 单轮对话的环境搭建参考博客 Text-to-SQL 工具Vanna + MySQL本地部署 | 数据库对话机器人 我的数据是这样 1. 基础配置 import vann…