【数据结构】二叉树和堆

news2024/9/25 20:39:01

文章目录

  • 一、 什么是二叉树
  • 二、 二叉树的存储结构
    • 顺序存储视图
  • 三、 堆
    • 堆的结构及概念
    • 大堆和小堆
  • 四、 建堆
  • 五、 堆排序
  • 六、 topk问题

一、 什么是二叉树

二叉树,作为一种重要的数据结构,由节点组成,每个节点可以有两个子节点,通常称为左子节点和右子节点。二叉树是有序的,树中包含的各个节点的度不能超过2,即只能是0、1或者2。
在这里插入图片描述
特殊二叉树

  1. 满二叉树
    一个二叉树,如果每一个层的结点数都达到最大值,则这个二叉树就是满二叉树。也就是
    说,如果一个二叉树的层数为K,且结点总数是 2的k次方-1,则它就是满二叉树。
  2. 完全二叉树
    完全二叉树,作为一种效率很高的数据结构,是由满二叉树衍生出来的。一棵深度为K且有n个结点的二叉树,如果其每个节点都与深度为K的满二叉树中编号从1至n的节点一一对应,则这棵二叉树被称为完全二叉树。
    在这里插入图片描述

二、 二叉树的存储结构

二叉树一般可以使用两种结构存储,一种顺序结构,一种链式结构。

  1. 顺序存储
    顺序结构存储就是使用数组来存储,一般使用数组只适合表示完全二叉树, 因为不是完全二叉树会有空间的浪费。二叉树顺序存储在物理上是一个数组,在逻辑上是一颗二叉树。
    用数组存储的方式更方便查找根和子树。
  2. 链式存储
    二叉树的链式存储结构是指,用链表来表示一棵二叉树,即用链来指示元素的逻辑关系。 通常的方法是
    链表中每个结点由三个域组成,数据域和左右指针域,左右指针分别用来给出该结点左孩子和右孩子所在的链结点的存储地址 。

顺序存储视图

在这里插入图片描述

三、 堆

堆的结构及概念

如果有一个关键码的集合K ={ k0,k1,k2……,k(n-1)}【0,1,2,……,n-1这些都是下标】,把它的所有元素按完全二叉树的顺序存储方式存储在一个一维数组中,并满足:Ki<=k(2i+1)且Ki<=k2i+2【Ki>=k(2i+1)且Ki>=k(2i+2)】i=0,1,2…,则称为小堆【或大堆】。将根节点最大的堆叫做最大堆或大根堆,根节点最小的堆叫做最小堆或小根堆。

堆的性质:

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

大堆和小堆

在这里插入图片描述
大堆:树中父亲的数据都大于等于孩子;
小堆:树中父亲的数据都小于等于孩子

四、 建堆

小堆为例
创建两个文件:
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 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 HeapSort(int* a, int n);
//交换位置
Swap(HPDataType* p1, HPDataType* p2);
//堆排序
void HeapSort(int* a, int n);
//向下交换
void AdjustDown(HPDataType* a, int n, int parent);
//向上查找
void Adjustup(HPDataType* a, HPDataType child);

Heap.c

#define   _CRT_SECURE_NO_WARNINGS 1
#include "Heap.h"
//堆的初始化
void HeapInit(Heap* hp)
{
	assert(hp);
	hp->a = NULL;
	hp->capacity = hp->size = 0;
}
// 堆的销毁
void HeapDestory(Heap* hp)
{
	assert(hp);
	free(hp->a);
	hp->a = NULL;
	hp->capacity = 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;
}
// 堆的判空
bool HeapEmpty(Heap* hp)
{
	assert(hp);
	return hp->size == 0;
}

//交换位置
Swap(HPDataType* p1, HPDataType* p2)
{
	HPDataType tmp = *p1;
	*p1 = *p2;
	*p2 = tmp;
}
//向上调整
Adjustup(HPDataType* a, HPDataType 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 HeapPush(Heap* hp, HPDataType x)
{
	assert(hp);
	//扩容
	if (hp->capacity == hp->size)
	{
		int newcapacity = hp->capacity == 0 ? 4 : hp->capacity * 2;
		HPDataType* tmp = (HPDataType*)realloc(hp->a, newcapacity * sizeof(HPDataType));
		if (tmp == NULL)
		{
			perror("realloc fail!");
			exit(1);
		}
		hp->a = tmp;
		hp->capacity = newcapacity;
	}
	hp->a[hp->size] = x;
	hp->size++;
	Adjustup(hp->a, hp->size - 1);//插入完数据后向上调整
}
//向下调整
AdjustDown(HPDataType* a, int n, int parent)
{
	int child = parent * 2 + 1;//假设左孩子小
	while (child < n)//child >= n,说明孩子已经调整到叶子了
	{
		//防止越界访问,找到小的那个孩子再向下调
		if (child + 1 < n && a[child] > a[child + 1])
		{
			++child;
		}
		if (a[child] < a[parent])
		{
			Swap(&a[child], &a[parent]);
			parent = child;//继续往下调整
			child = parent * 2 + 1;
		}
		else
		{
			break;
		}
	}
}

// 堆的删除
void HeapPop(Heap* hp)
{
	//Pop删除堆顶的数据(根位置)
	assert(hp);
	assert(hp->size > 0);
	//不能直接暴力删除,否则关系会乱套,兄弟变父子
	Swap(&hp->a[0], &hp->a[hp->size-1]);//第一个数据和最后一个数据交换
	hp->size--;
	AdjustDown(hp->a, hp->size, 0);//删除完需要用到向下调整算法
}

五、 堆排序

void HeapSort(int* a, int n)
{
	// 降序,建小堆
	// 升序,建大堆
	//建小堆
	for (int i = 1; i < n; i++)
	{
		AdjustUp(a, i);
	}
	
	int end = n - 1;
	while (end > 0)
	{
		Swap(&a[0], &a[end]);//将最小的数和最后的数进行交换
		AdjustDown(a, end, 0);
		--end;
	}
}
int main()
{
	int a[] = { 4,2,8,1,5,6,9,7 };
	HeapSort(a, sizeof(a) / sizeof(0));
}

调试更能理解这个过程
在这里插入图片描述

六、 topk问题

TOP-K问题:即求数据结合中前K个最大的元素或者最小的元素,一般情况下数据量都比较大。

比如:专业前10名、世界500强、富豪榜、游戏中前100的活跃玩家等。

对于Top-K问题,能想到的最简单直接的方式就是排序,但是:如果数据量非常大,排序就不太可取了(可能数据都不能一下子全部加载到内存中)。最佳的方式就是用堆来解决,基本思路如下:

假如我们现在想要找到100亿个整数中,取出前K个最大的数,如果我们直接对100亿个整数进行排序至少需要40G的内存,这会造成空间上巨大的浪费。

我们可以先取K个数建立一个小堆,再将后100亿-K个数依次与堆顶元素相比较,如果比堆顶元素大就将其替换后重新向下调整为一个小堆,再接着与下一个数相比,这样最终就可以找到前K个最大的整数了。(节约了大量的空间)

该方法的时间复杂度为:O(N*㏒⑵K)

空间复杂度为O(K)

![void CreateNDate()
{
	// 造数据
	int n = 100000;
	srand(time(0));
	const char* file = "F:\\vs2022\\二叉树_堆\\二叉树_堆\\data.txt";
	FILE* fin = fopen(file, "w");
	if (fin == NULL)
	{
		perror("fopen error");
		return;
	}

	for (int i = 0; i < n; i++)
	{
		int x = (rand() + i);
		fprintf(fin, "%d\n", x);
	}

	fclose(fin);
}

void PrintTopK()
{
	int k = 0;
	printf("请输入要查找的前K个值>:");
	scanf("%d", &k);
	int* kminheap = (int*)malloc(sizeof(int) * k);
	if (kminheap == NULL)
	{
		printf("malloc fali");
		return;
	}
	const char* file = "data.txt";
	FILE* fout = fopen(file, "r");
	if (fout == NULL)
	{
		printf("fopen error");
		return;
	}
	
	
	//读取前k个数
	for (int i = 0; i < k; i++)
	{
		fscanf(fout, "%d", &kminheap[i]);
	}
	//建堆
	for (int i = (k - 1 - 1) / 2; i >= 0; i--)
	{
		AdjustDown(kminheap, k, i);
	}
	//读取剩下的N-K个数
	int x = 0;
	while (fscanf(fout, "%d", &x) > 0)
	{
		if (x > kminheap[0])
		{
			kminheap[0] = x;
		}
		AdjustDown(kminheap, k, 0);
	}
	for (int i = 0; i < k; i++)\
	{
		printf("%d ", kminheap[i]);
	}
	printf("\n");
}

——————————————————————————————————————————
希望这篇博客对你有所帮助!!!
在这里插入图片描述

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

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

相关文章

【UE5.1 角色练习】07-AOE技能

目录 效果 步骤 一、准备技能动画 二、准备粒子特效 三、技能蓝图 四、相机震动 前言 在上一篇&#xff08;【UE5.1 角色练习】06-角色发射火球-part2&#xff09;基础上继续实现角色释放AOE技能的功能。 效果 步骤 一、准备技能动画 1. 在项目设置中添加一个操作映…

Compose Multiplatform 1.6.10 发布,解释一些小问题, Jake 大佬的 Hack

虽然一直比较关注跨平台开发&#xff0c;但其实我很少写 Compose Multiplatform 的内容&#xff0c;因为关于 Compose Multiplatform 的使用&#xff0c;其实我并没在实际生产环境上发布过&#xff0c;但是这个版本确实值得一提&#xff0c;因为该版本包含&#xff1a; iOS Bet…

Vue基础(数据绑定、export使用)

1、简介 在使用vue开发的过程中&#xff0c;经常会遇到一些容易混淆的问题&#xff0c;因此&#xff0c;在本文中进行汇总操作&#xff0c;只有通过不断总结学习&#xff0c;才能更好掌握vue的使用&#xff08;每天进步一点&#xff09;。 2、数据绑定 在js中定义数据&#xf…

基于深度学习和opencv的车牌识别系统

免费获取方式↓↓↓ 项目介绍028&#xff1a; 基于深度学习和opencv的车牌识别系统 同时利用对图片每一帧图像加入视频分析模块 图片分析模块可以依据界面按钮提示进行相应功能 视频分析模块可以根据按钮提示进行对视频的分析 &#xff08;视频模块的视频追踪处理时间较长&…

知攻善防应急响应靶机训练-Web2

前言&#xff1a; 本次应急响应靶机采用的是知攻善防实验室的Web-2应急响应靶机 靶机下载地址为&#xff1a; https://pan.quark.cn/s/4b6dffd0c51a 相关账户密码 用户:administrator 密码:Zgsfqq.com 解题过程&#xff1a; 一、攻击者的IP地址&#xff08;两个&#xff09;…

1108 String复读机

solution1 分别统计字符String的个数&#xff0c;并按照该顺序输出 #include<iostream> #include<string> #include<map> using namespace std; map<char, int> mp; void handle(char c){if(mp.count(c)){cout << c;mp[c]--;if(mp[c] 0) mp.e…

【软件开发规范篇】前言

作者介绍&#xff1a;本人笔名姑苏老陈&#xff0c;从事JAVA开发工作十多年了&#xff0c;带过大学刚毕业的实习生&#xff0c;也带过技术团队。最近有个朋友的表弟&#xff0c;马上要大学毕业了&#xff0c;想从事JAVA开发工作&#xff0c;但不知道从何处入手。于是&#xff0…

吴恩达2022机器学习专项课程C2W2:2.22 多类 softmax softmax与神经网络 softmax的代码改良 多标签分类

目录 多分类问题1.什么是多分类问题2.多分类问题案例3.二分类与多分类的区别 Softmax1. 什么是Softmax2.逻辑回归预测的计算过程3. Softmax预测的计算过程4.Softmax 回归与逻辑回归的关系5. Softmax的损失函数 softmax与神经网络1.设置Softmax层2.Softmax层的计算3.softmax激活…

全国多地入夏!对抗“高温高湿”约克VRF中央空调有妙招

随着气温飙升,北京、上海、广州、南京、天津、江苏、新疆、内蒙古部分地区等多地进入夏季状态,华北、黄淮等不少地方最高气温都超过了30℃,大街上人们短袖、短裤纷纷上阵,一派夏日炎炎的景象。 炎热夏季不仅高温频频来袭,往往还伴随着降雨带来的潮湿,天气湿热交织容易让人们身…

开发心电疾病分类的深度学习模型并部署运行于ARM虚拟硬件平台(AVH)

目录 一、ARM虚拟硬件平台介绍 二、心电疾病分类模型介绍 三、部署流程 3.1 基于百度云平台订阅虚拟硬件镜像 3.2 安装编译相关组件 3.1 数据加载 3.2 模型转换 方式一&#xff1a; tensorflow模型转换为onnx模型&#xff0c;onnx模型转换为TVM模型 方式二&#xff1…

分子对接 molecular docking

https://www.sciencedirect.com/science/article/pii/S094471132400374X?via%3Dihub GitHub - beikwx/SailVina: SailVina重构增强版 Molecular docking Download the PTPRB protein structure on the PDB database (RCSB PDB: Homepage). Select the high-resolution PTP…

Dubbo生态之sentinel限流

1. 限流算法 我们知道&#xff0c;在分布式架构中&#xff0c;当服务请求量过大时&#xff0c;容易对服务器造成不可预知的压力&#xff0c;因此&#xff0c;我们在客户端请求的时候&#xff0c;进行限流&#xff0c;起到一个保护的作用 常见的限流算法有: 计数器限流&#x…

Mixed-precision计算原理(FP32+FP16)

原文&#xff1a; https://lightning.ai/pages/community/tutorial/accelerating-large-language-models-with-mixed-precision-techniques/ This approach allows for efficient training while maintaining the accuracy and stability of the neural network. In more det…

C++第二十弹---深入理解STL中vector的使用

✨个人主页&#xff1a; 熬夜学编程的小林 &#x1f497;系列专栏&#xff1a; 【C语言详解】 【数据结构详解】【C详解】 目录 1、vector的介绍 2、vector的使用 2.1、构造函数和赋值重载 2.1.1、构造函数的介绍 2.1.2、代码演示 2.2、容量操作 2.3、遍历 2.4、增删…

RH850F1KM-S4-100Pin_ R7F7016453AFP MCAL PWM 配置

1、PWM组件包含的子配置项 PwmGeneralPwmDemEventParameterRefsPwmConfigurationOfOptApiServicesPwmChannelConfigSet2、PwmGeneral 2.1、PwmPowerStateConfig 2.1.1、PwmPowerState 该参数的每个实例描述PWM HW支持的不同功率状态。它应该由硬件供应商定义,并由PWMDriver用…

HoneyTrap蜜罐系统实践操作@FreeBSD

HoneyTrap介绍 HoneyTrap是一个可扩展的开源系统&#xff0c;用于运行、监控和管理蜜罐。 HoneyTrap蜜罐系统通过在网络中部署感应节点&#xff0c;实时感知周边网络环境&#xff0c;并将感应节点的日志进行实时存储和可视化分析&#xff0c;从而实现对网络环境中威胁情况的感…

BUUCTF-WEB3

[极客大挑战 2019]Knife1 1.打开附件链接 一句话木马eval($_POST["Syc"]); 2.中国蚁剑 用中国蚁剑连接 在根目录下找到一个名为flag的文件 3.得到flag [极客大挑战 2019]Upload1

STM32Cubemx HAL库 移植FreeRTOS源码

本篇文章主要是使用STM32Cubemx生成Keil工程&#xff0c;然后在移植FreeRTOS源码&#xff0c;最后测试使用。 一、FreeRTOS简介 Free 和 RTOS&#xff0c;Free 就是免费的、自由的、不受约束的意思&#xff0c;RTOS 全称是 Real Time Operating System&#xff0c;中文名就是实…

大数据开发面试题【Spark篇】

115、Spark的任务执行流程 driver和executor&#xff0c;结构式一主多从模式&#xff0c; driver&#xff1a;spark的驱动节点&#xff0c;用于执行spark任务中的main方法&#xff0c;负责实际代码的执行工作&#xff1b;主要负责&#xff1a;将代码逻辑转换为任务、在executo…

Java对象不再使用时,为什么要赋值为 null ?

在Java中&#xff0c;将不再使用的对象赋值为null的目的主要是为了帮助垃圾收集器&#xff08;更快地释放内存。我这里有一套编程入门教程&#xff0c;不仅包含了详细的视频讲解&#xff0c;项目实战。如果你渴望学习编程&#xff0c;不妨点个关注&#xff0c;给个评论222&…