【数据结构和算法初阶(C语言)】二叉树的顺序结构--堆的实现/堆排序/topk问题详解---二叉树学习日记②

news2024/11/13 10:37:45

目录

​编辑

1.二叉树的顺序结构及实现

1.1 二叉树的顺序结构

2 堆的概念及结构

3 堆的实现

3.1堆的代码定义

3.2堆插入数据

3.3打印堆数据

3.4堆的数据的删除

3.5获取根部数据

3.6判断堆是否为空

3.7 堆的销毁 

4.建堆以及堆排序 

4.1 升序建大堆,降序建小堆

4.2堆排序

4.3 topk问题

5.结语


1.二叉树的顺序结构及实现

1.1 二叉树的顺序结构

普通的二叉树是不适合用数组来存储的,因为可能会存在大量的空间浪费。而完全二叉树更适合使用顺序结 构存储。现实中我们通常把堆(一种二叉树)使用顺序结构的数组来存储,需要注意的是这里的堆和操作系统 虚拟进程地址空间中的堆是两回事,一个是数据结构,一个是操作系统中管理内存的一块区域分段。

左孩子的下标 = 父亲下标*2+1

右孩子下标 = 父亲节点下标*2+2

父亲节点下标 = (子节点下标-1)/2 

2 堆的概念及结构

堆是非线性结构,是完全二叉树

如果有一个值的集合K = { , , ,…, },把它的所有元素按完全二叉树的顺序存储方式存储 在一个一维数组中,并满足: = 且 >= ) i = 0,1, 2…,则称为小堆(或大堆)。将根节点最大的堆叫做最大堆或大根堆,根节点最小的堆叫做最小堆或小根堆。 堆的性质: 堆中某个节点的值总是不大于或不小于其父节点的值;

堆总是一棵完全二叉树。

通俗来说父节点小于等于子节点的完全二叉树就叫小根堆,或者小堆,根一定是整棵树最小的。

父节点值大于等于子节点的完全二叉树叫做大根堆。或者大堆,但是底层数组不一定降序。但是大堆的根是整棵树的最大值。

3 堆的实现

3.1堆的代码定义

底层是一个顺序表

typedef int HPDataType;

typedef struct Heap
{
	//底层是一个顺序表,但是数据不是随便存储,逻辑结构是二叉树
	HPDataType * a;
	int size;
	int capacity;
}HP;

堆的初始化:

void HeapInit(HP* php)
{
	assert(php);
	HPDataType* tmp = (HPDataType*)malloc(sizeof(HPDataType) * 2);//先为i堆空间申请两个节点
	if (tmp == NULL)
	{
		perror("malloc");
		exit(-1);
	}
	
	php->a = tmp;
	php->capacity = 2;
	
	php->size = 0;
}

 

3.2堆插入数据

实现关键

实现原理图:向上调整:

(以大堆的实现方式举例)

首先我们从有限个数据的层面来实现一下堆的实现,后面堆排序再来看对于一堆数据怎么建堆。

对于一组少量数据比如一个数组:

首先将数据一个一个插入到堆里面,由于数据有限可以使用这种数据插入的方式建立堆这种数据结构;

void HeapPush(HP* php, HPDataType x)
{
	//尾插

	assert(php);
	//判断空间够不够
	if (php->capacity == php->size)
	{
		HPDataType* tmp = (HPDataType*)realloc(php->a, sizeof(php->a) + sizeof(HPDataType) * 2);
		if (tmp == NULL)
		{
			perror("realloc");
			exit(-1);
		}
		php->a = tmp;
		php->capacity += 2;
	}
	php->a[php->size] = x;
	php->size++;
	//调整数据,变成堆
	AdjustUp(php->a, php->size-1);
	
}

 然后把这组数据调整成一个堆:

 

void Swap(HPDataType* child, HPDataType* parent)
{
	HPDataType tmp = 0;
	tmp = *child;
	*child = *parent;
	*parent = tmp;
}
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 = (parent - 1) / 2;
		}
		else
		{
			break;//跳出循环
		}
	}

}

3.3打印堆数据

为了看一下我们插入的效果我们来试一下插入一段数据 

 

void HeapPrint(HP* php)
{
	assert(php); 
	for (int i = 0; i < php->size; i++)
	{
		printf("%d ", php->a[i]);
		
	}
}

 

 就建成了一个大堆。

3.4堆的数据的删除

堆这个数据结构有意义的一个点就是,大堆的根一定是这组数据中最大的值,小堆的根一定是这组数据中最小的值。所以如果我们能拿到这个根的数据,再删除就可以找到这堆数据中次小的数据了。那么删除根数据是这个结构比较有意义的。

想一个问题:根的删除能不能简单的数据覆盖?只是将后续的数据移动向前

答案是不能的,可以数据这样移动后续数据根本就不能成堆了。那么这里使用的方法是向下调整法

前提是左右子树是堆:

这里我们以小堆举例示范:

先删除

void HeapPop(HP* php) 
{
	assert(php);
	//不可挪动覆盖。可能就不是堆了
	//先交换根和最后一个值,再删除,左右子树依旧是小堆
	//向下调整的算法,左右子树都是小堆或者大堆。
	 
	assert(php->size > 0);
	Swap(&php->a[0],&php->a[php->size-1]);
	php->size--;//删除了数据
	AdjustDown(php->a,php->size, 0);
}

在调整

void AdjustDown(HPDataType* a, int n, int parent)
{
	int child = parent * 2 + 1;
	while (child<n)
	{
		if (child+1<n&&a[child + 1] < a[child])//child+1可能越界访问
		{
			child++;
		}
		if (a[child] < a[parent])
		{
			Swap(&a[child], &a[parent]);
			//继续向下调整
			parent = child;
			child = parent * 2 + 1;
		}
		else
		{
			break;
		}
	}

}

调整是由于每次都是取两个子节点中的较小的值,所以先假设一个大,如果假设错了,就改变下标 

if (child+1<n&&a[child + 1] < a[child])//child+1可能越界访问
        {
            child++;
        }

对调整循环结束的判定所示孩子下标小于n

3.5获取根部数据

//获取根部数据
HPDataType HeapTop(HP* php)
{
	assert(php);
	assert(php->size > 0);
	return php->a[0];
}

3.6判断堆是否为空


//判断堆是否为空函数
bool HeapEmpty(HP* php)
{
	assert(php);
	return php->size == 0;

}

3.7 堆的销毁 

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

那么如果现在我们每次拿到堆的元素在删除在获取,就可以得到一个有序的数据了:

4.建堆以及堆排序 

上面我们已经掌握了堆这个数据结构的一些方法,最后通过插入数据建堆。删除1数据将数据排序。可是如果我有十亿个数据,想找出最大的十个数据,如果用堆得插入10亿次数据吗?那就失去了使用这个数据结构的意义,通常来说我们只用建立一个大堆模型,这个堆的前十个数据自然就是10亿个数据中的最大的一个。

4.1 升序建大堆,降序建小堆

4.2堆排序

4.3 topk问题

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

比如:专业前10名、世界500强、富豪榜、游戏中前100的活跃玩家等。 对于Top-K问题,能想到的最简单直接的方式就是排序,但是:如果数据量非常大,排序就不太可取了(可能 数据都不能一下子全部加载到内存中)。最佳的方式就是用堆来解决,

基本思路如下:

1. 用数据集合中前K个元素来建堆 前k个最大的元素,则建小堆 前k个最小的元素,则建大堆

2. 用剩余的N-K个元素依次与堆顶元素来比较,不满足则替换堆顶元素将剩余N-K个元素依次与堆顶元素比完之后,堆中剩余的K个元素就是所求的前K个最小或者最大的元素。

(明天补)

5.结语

以上就是本期的所有内容,知识含量蛮多,大家可以配合解释和原码运行理解。创作不易,大家如果觉得还可以的话,欢迎大家三连,有问题的地方欢迎大家指正,一起交流学习,一起成长,我是Nicn,正在c++方向前行的奋斗者,数据结构内容持续更新中,感谢大家的关注与喜欢。

 

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

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

相关文章

python自定义日历库,与对应calendar库函数功能基本一致

目录 自定义日历库 常用列表 日期列表 常用函数 闰年判断 月份天数 元旦序号 日历表头 星期序号 序号及天数 月历字串 打印月历 年历字串 打印年历 对比测试 测试结果 完整代码 运行结果 自定义日历库 自定义日历库函数&#xff0c;并使得其与python calend…

学习笔记 | 微信小程序项目day03

今日学习内容 配置自定义导航栏通用轮播组件通用的轮播图组件完善以及主页调用分类面板以及热门推荐面板猜你喜欢模块&#xff08;分页查询&#xff09;首页下拉刷新首页骨架屏 配置自定义导航栏 1、创建自定义组件 /index/components/CustomNavbar.vue <script setup l…

安捷伦agilent dso9254a示波器

181/2461/8938产品概述&#xff1a; 安捷伦DSO9254A示波器配有15英寸XGA显示器&#xff0c;封装厚度仅为9英寸&#xff08;23厘米&#xff09;&#xff0c;重量仅为26磅&#xff08;11.8千克&#xff09;&#xff0c;以节省您有限的工作台空间。该范围旨在为您提供最广泛的测量…

酷开科技与您共筑希望,酷开系统助力孩子成长启航

​父母是孩子的第一任老师&#xff0c;家庭是孩子的第一所学校。良好的家庭教育&#xff0c;对于孩子人格的形成以及终身教育有着不可估量的作用。每一个优秀孩子的背后&#xff0c;都有一个优秀的家庭。那么怎么才能让孩子健康快乐的接受知识呢&#xff1f;酷开系统中为孩子打…

文件的创建与删除

文件的创建 使用File类创建一个文件对象&#xff0c;例如&#xff1a;File filenew File("c:\\myletter" , "letter.txt"); public boolean createNewFile();/*如果c:\myletter目录中没 有名字为letter.txt文件&#xff0c;文件对象file调用createNewFil…

Python和R的区别是什么,Python与R的应用场景是什么?

如果你这么问&#xff0c;那么你可能正站在数据科学的起点。对于志在成为数据专业人员的你来说&#xff0c;学习编程是无疑的。我想行你早就听过Python 与R的比较之声&#xff0c;并在选择中感到困惑。在此&#xff0c;我想说&#xff0c;也算是一种安慰吧&#xff1a;对于语言…

Zynq—AD9238数据采集DDR3缓存千兆以太网发送实验(后记)

2024.03.05&#xff1a; 测试了开发板网线直连电脑可以传输数据。但是通过开发板→交换机→电脑&#xff0c;没有数据传输过去。通讯采用UDP通讯。首先是UDP传输不可靠&#xff0c;有可能存在丢包、包先后顺序有问题&#xff0c;这就无法满足后续对采集数据的傅里叶变换和傅里…

实不相瞒,我抖音小店三月的收入,可能是你半年的收入

大家好&#xff0c;我是电商花花。 很难想象吧&#xff0c;抖音小店能做起来也挺赚钱的。 钱这对于一个人来说&#xff0c;有多重要&#xff1f; 相信大家也都很清楚&#xff0c;没有钱的支撑&#xff0c;当生活给你一巴掌&#xff0c;我们根本无力回击。 但是绝大数人的常…

3D地理空间数据

过去十年中&#xff0c;地理空间革命在采集硬件、处理软件和云技术方面取得了重大进展。 新用户发现地理空间技术越来越容易使用。 随着数据可用性&#xff08;新获取的数据或开放公共档案&#xff09;的增加&#xff0c;沟通结果的需求也随之增加。 2D&#xff08;或 2.5D&…

【LeetCode 算法刷题笔记-路径篇】

1.0112. 路径总和 1.1 题目大意 描述&#xff1a;给定一个二叉树的根节点 root 和一个值 targetSum。 要求&#xff1a;判断该树中是否存在从根节点到叶子节点的路径&#xff0c;使得这条路径上所有节点值相加等于 targetSum。如果存在&#xff0c;返回 True&#xff1b;否则…

三 C#插入排序算法

简介 插入排序算法是一种简单、直观的排序算法&#xff0c;其原理是将一个待排序的元素逐个地插入到已经排好序的部分中。 插入排序实现原理 插入排序算法是一种简单、直观的排序算法&#xff0c;其原理是将一个待排序的元素逐个地插入到已经排好序的部分中。 具体实现步骤…

鸿蒙Harmony应用开发—ArkTS声明式开发(绘制组件:Rect)

矩形绘制组件。 说明&#xff1a; 该组件从API Version 9开始支持。后续版本如有新增内容&#xff0c;则采用上角标单独标记该内容的起始版本。 子组件 无 接口 Rect(value?: {width?: string | number,height?: string | number,radius?: string | number | Array<s…

用 二层口 实现三层口 IP 配置的一个实现方法

我们一般用 undo portswitch 来将二层口转为三层口&#xff0c;但如果设备不支持的话&#xff0c;那么。。。 一、拓朴图&#xff1a; 二、实现方法&#xff1a; 起一个 vlan x&#xff0c;配置 vlanif地址&#xff0c;然后二层口划分到 vlan x 下&#xff0c;对端做同样的配置…

windows server 下的mysql 8.0.28修改数据库目录

1. 查看当前数据库存储位置 show global variables like %datadir%; 默认是&#xff1a;C:\ProgramData\MySQL\MySQL Server 8.0\Data 2. 修改 C:\ProgramData\MySQL\MySQL Server 8.0\my.ini配置文件。如下&#xff1a; datadirD:/ProgramData/MySQL/MySQL Server 8.0/Dat…

计算机网络——物理层(信道复用技术)

计算机网络——物理层&#xff08;信道复用技术&#xff09; 信道复用技术频分多址与时分多址 频分复用 FDM (Frequency Division Multiplexing)时分复用 TDM (Time Division Multiplexing)统计时分复用 STDM (Statistic TDM)波分复用码分复用 我们今天接着来看信道复用技术&am…

MPIKGC:大语言模型改进知识图谱补全

MPIKGC&#xff1a;大语言模型改进知识图谱补全 提出背景MPIKGC框架 论文&#xff1a;https://arxiv.org/pdf/2403.01972.pdf 代码&#xff1a;https://github.com/quqxui/MPIKGC 提出背景 知识图谱就像一个大数据库&#xff0c;里面有很多关于不同事物的信息&#xff0c;这…

UnityShader(十八) AlphaBlend

上代码&#xff1a; Shader "Shader入门/透明度效果/AlphaBlendShader" {Properties{_MainTex ("Texture", 2D) "white" {}_AlphaScale("AlphaScale",Range(0,1))1.0}SubShader{Tags { "RenderType""Transparent&quo…

LeetCode每日一题【54.螺旋矩阵】

思路&#xff1a;模拟&#xff0c;初始化上下左右4个方向的边界&#xff0c;逐步收缩&#xff0c;注意要及时判断元素是否已经放满&#xff0c;否则会放入多余元素 class Solution { public:vector<int> spiralOrder(vector<vector<int>>& matrix) {int…

YOLOv9改进策略:下采样涨点系列 | 一种新颖的基于 Haar 小波的下采样HWD,有效涨点系列

&#x1f4a1;&#x1f4a1;&#x1f4a1;本文独家改进&#xff1a;HWD的核心思想是应用Haar小波变换来降低特征图的空间分辨率&#xff0c;同时保留尽可能多的信息&#xff0c;与传统的下采样方法相比&#xff0c;有效降低信息不确定性。 &#x1f4a1;&#x1f4a1;&#x1…

MATLAB教程

目录 前言一、MATLAB基本操作1.1 界面简介1.2 搜索路径1.3 交互式命令操作1.4 帮助系统 二、MATLAB语言基础2.1 数据类型2.2 MATLAB运算2.2.1 算数运算2.2.2 关系运算2.2.3 逻辑运算 2.3 常用内部函数2.4 结构数据与单元数据 三、MATLAB程序设计3.1 M文件3.2 函数文件3.3 程序控…