上期回顾:【数据结构】树(堆)·上

一.堆的应用

1.1堆排序(向下调整在上一期)

向上调整算法建堆:

首先先回顾一下向上调整算法

void AdjustUP(HPDataType* arr, int child)
{int parent = (child - 1) / 2;//找到父结点的位置while (child > 0)//循环,确保父结点一定小于(大于)子结点{//小堆:<//大堆:>//     |//     Vif (arr[child] > arr[parent]){//调整位置Swap(&arr[child], &arr[parent]);child = parent;parent = (child - 1) / 2;}elsebreak;}
}

在上一期我们也学过了向下调整算法建堆,其是通过由下向上循环,通过确保父结点下方的是堆结构,而向上算法建队=堆就刚好反过来,是由上到下进行循环。

//堆排序
void HeapSort(int *arr,int n)
{//建堆--向下调整for (int i = (n - 1 - 1) / 2; i >= 0; i--){AdjustDOWN(arr, i, n);}//建堆--向上调整for (int i = 0; i < n; i++){AdjustUP(arr, i);}//排序int end = n - 1;while (end > 0){Swap(&arr[end], &arr[0]);AdjustDOWN(arr, 0, end);end--;}
}

1.2.TOP-K问题

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

通过前面的学习可了解到,小堆的堆顶是数据的最小值,所以若想通过小堆找最小,就需要遍历所有的数据,在进行堆排序,这对于大数据肯定是不行的,

那么换一种思路,用小堆找最大数据:将前K个数据直接建小堆,然后再遍历剩下的n-k个数据,将其与堆顶进行比较,若比堆顶大,则替换堆顶,来回重复,最后构成一个新堆,该堆就是数据中前K大的数。

而找前K小的数据道理相同,所以就可以总结出:

找最大的前K个数据,建小堆

找最小的前K个数据,建大堆

实践:

先通过代码生成10万个数据,并保存在data.txt文件中

随后实现topK函数:

void TopK()
{int k =0;printf("请输入k:");scanf("%d", &k);const char* file = "data.txt";FILE* fout = fopen(file, "r");if (fout == NULL){perror("fopen fail");exit(1);}//找最大的前K个数据--建小堆//找最小的前K个数据--建大堆int* minHeap = (int*)malloc(sizeof(int) * k);if (minHeap == NULL){perror("malloc fail");exit(1);}for (int i = 0; i < k; i++){fscanf(fout, "%d", &minHeap[i]);}//minHeap -- 向下调整建堆for (int i = (k - 2) / 2; i >=- 0; i--){//找最大的前K个数据--建小堆//找最小的前K个数据--建大堆AdjustDOWN(minHeap, i, k);}//遍历剩下的n-k个数据,跟堆顶比较,堆顶小替换堆顶数据int x = 0;while (fscanf(fout, "%d", &x) != EOF){//找最大>//找最小<if (x > minHeap[0]){minHeap[0] = x;AdjustDOWN(minHeap, 0, k);}}for (int i = 0; i < k; i++){printf("%d ", minHeap[i]);}printf("\n");fclose(fout);
}

先读取文件数据,取前K个保存在数组中,并构成堆,随后遍历剩下的n-k个数据,进行比较替换。

最后成功打印

二.实现链式结构二叉树

二叉树的相关知识需要用到递归知识,所以建议先学习递归后再来看以下文章!!!


2.1二叉树的插入与删除

数据的插入和删除可以在二叉树的任何位置,所以此时暂时不写文章,在后期学习到平衡树,红黑树等在进行编写。

2.2二叉树的遍历

二叉树的遍历有前序/中序/后序的遍历的递归结构遍历:

1)前序遍历:访问根节点的操作在遍历其左右子树之间

 访问顺序为:根结点、左子树、右子树

2)中序遍历:访问根结点的操作发生在遍历其左右子树之中(间)

访问顺序为:左子树、根结点、右子树

3)后序遍历:访问根结点的操作发生在遍历其左右子树之后

访问顺序为:左子树、右子树、根结点

遍历的实现:

通过函数的递归实现遍历,需要先判断递归条件,当目前根节点为NULL时,递归中止,拿前序遍历为例,在目前根节点时,先访问根节点的data,随后根据前序遍历的顺序,分别访问左子树,右子树,进行递归。那么中序遍历与后序遍历也就很好写出来了。

构造文章上文的二叉树例子,然后进行遍历输出:

2.3二叉树的结点个数

1.运用二叉树遍历的知识,通过递归遍历每一个结点,然后size++,那么结果会是什么呢?

int BinaryTreeSize(BTNode* root)
{int size = 0;if (root == NULL){return;}//结点非空,+1size++;BinaryTreeSize(root->left);BinaryTreeSize(root->right);return size;
}

运行后可以发现,不管调用几次,size都为1,原因是因为,每次递归size都被初始化为0

2.那么把size设为全局变量后是不是就可以避免了?

int size = 0;
int BinaryTreeSize(BTNode* root)
{if (root == NULL){return;}//结点非空,+1size++;BinaryTreeSize(root->left);BinaryTreeSize(root->right);return size;
}

运行后发现,虽然第一次调用size的结果是正确的,但是经过多次调用,size的值就会越加越大。

3.这次换一种思路,直接把size设为参数进行调用。

int BinaryTreeSize(BTNode* root, int size)
{if (root == NULL){return;}//结点非空,+1size++;BinaryTreeSize(root->left,size);BinaryTreeSize(root->right,size);return size;
}

这一次发现结果又变为三个1,分析原因,当递归回溯的时候,size的值会返回到上一步的值,例如当递归到4结点时,size=3,但是回溯到2结点时,size又回到了2

4.要解决这个问题,把传值调用改为传址调用就可解决。

int BinaryTreeSize(BTNode* root,int*size)
{if (root == NULL){return 0;}//结点非空,+1(*size)++ ;BinaryTreeSize(root->left,size);BinaryTreeSize(root->right,size);return *size;
}

这时结果就正确了,但是,每次调用参数,都需要把size初始化为0,有一点麻烦,在对函数进行改造

分析二叉树可知,结点数 = 根结点+左子树的结点数+右子树的结点数 

最终版:

//二叉树节点个数
int BinaryTreeSize(BTNode* root)
{if (root == NULL){return 0;}//结点非空,+1return 	1 + BinaryTreeSize(root->left) + BinaryTreeSize(root->right);
}

2.4二叉树叶子结点数

叶子结点数 = 左子树叶子节点数 + 右子树叶子结点数

结合刚才学到的结点数的知识,可以写出:

//二叉树叶子节点数
int BinaryTreeLeafSize(BTNode* root)
{if (root == NULL){return 0;}if (root->left == NULL && root->right == NULL){return 1;}return BinaryTreeLeafSize(root->left) + BinaryTreeLeafSize(root->right);
}

2.5二叉树第K层结点个数

//二叉树第K层结点个数
int BinaryTreeLevelSize(BTNode* root, int k)
{if (root == NULL){return 0;}if (k == 1){return 1;}return BinaryTreeLevelSize(root->left, k - 1) + BinaryTreeLevelSize(root->right, k - 1);
}

2.6二叉树的深度

深度 = 根结点 + max(左子树的深度+右子树的深度)

//二叉树的深度
int BinaryTreeDepth(BTNode* root)
{if (root == NULL){return 0;}return 1 + max(BinaryTreeDepth(root->left), BinaryTreeDepth(root->right));//return 1 + (BinaryTreeDepth(root->left) > BinaryTreeDepth(root->right) ? BinaryTreeDepth(root->left) : BinaryTreeDepth(root->right));
}

2.7二叉树查找值为x的结点

//二叉树查找值为x的结点
BTNode* BinaryTreeFind(BTNode* root, BTDtatatype x)
{if (root == NULL){return NULL;}if (root->data == x){return root;}BTNode*leftFind =  BinaryTreeFind(root->left, x);if (leftFind){return leftFind;}BTNode*rightFind =  BinaryTreeFind(root->right, x);if (rightFind){return rightFind;}
}

2.8二叉树的销毁

//二叉树的销毁
void BinaryTreeDestroy(BTNode** root)
{if (root == NULL){return;}BinaryTreeDestroy(&((*root)->left));BinaryTreeDestroy(&((*root)->right));free(*root);*root = NULL;
}

二叉树的层序遍历

借助数据结构--队列

首先将之前所编写过的队列的.h和.c文件导入进来

注意:

1.要包含队列的头文件

2.修改队列的头文件:修改队列的保存类型,务必要加struct,为了告诉编译器BinaryTreeNode是个结构体。

2.9层序编列实现:

//层序遍历
void levelOrder(BTNode* root)
{Queue q;QueueInit(&q);QueuePush(&q, root);while (!QueueEmpty(&q)){//取队头,出队头BTNode*top =  QueueFront(&q);QueuePop(&q);printf("%c ", top->data);//将左右孩子入队列if (top->left)QueuePush(&q, top->left);if (top->right)QueuePush(&q, top->right);}QueueDestrory(&q);
}

2.10判断是否为完全二叉树

前置知识:完全二叉树的特点:

1)最后一层节点个数不一定达到最大

2)结点从左到右依次排列

与二叉树的层序遍历相同,借助队列进行实现:

在第一个循环中,取队头,出对头,直到遇到第一个空结点出循环,并进入到第二个循环。

若在第二个循环中,从非空队列中取到了非空的队头结点,那么该二叉树一定不是完全二叉树,否则一定是完全二文树

//判断是否为完全二叉树
bool BinaryTreeComlete(BTNode* root)
{Queue q;QueueInit(&q);QueuePush(&q, root);while (!QueueEmpty(&q)){//取队头,出队头BTNode* top = QueueFront(&q);QueuePop(&q);if (top == NULL){break;}QueuePush(&q, top->left);QueuePush(&q, top->right);}//队列不一定为空while(!QueueEmpty(&q)){BTNode* top = QueueFront(&q);QueuePop(&q);if (top != NULL){return false;}}QueueDestrory(&q);return true;
}

以上图做例子,应不是完全二叉树:符合预期

把G H I结点去掉后应为完全二叉树:符合预期

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

如若内容造成侵权/违法违规/事实不符,请联系英文站点网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Elasticsearch MCP 服务器现已在 AWS Marketplace 上提供

作者&#xff1a;来自 Elastic Udayasimha Theepireddy (Uday), Matt Ryan, Srinivas Pendyala 我们很高兴地宣布&#xff0c;Elasticsearch Model Context Protocol&#xff08; MCP &#xff09;服务器现已在 AWS Marketplace 上提供。 使用 MCP 将代理连接到 Elasticsearch …

【Linux】Makefile(一)-介绍

Makefile 本篇博客是作者在学习Linux方面知识过程中&#xff0c;对Makefile片面的了解&#xff0c;从而产生了对Makefile有一个全面的认识的想法&#xff0c;在知道《跟我一起写Makefile》此书后&#xff0c;作者学习阅读过程中整理出的笔记。 目录Makefilemakefile介绍:规则&…

Java爬虫与正则表达式——用正则来爬取数据

APIJava帮我们写好的各种功能的Java类。这些Java类统称为API。正则表达式就是API帮我们写好的类。正则表达式例子&#xff1a; 字符类&#xff1a;[abc]&#xff1a;只能是a&#xff0c;b或c[^abc]&#xff1a;除了a&#xff0c;b&#xff0c;c之外的任何字符[a-zA-Z]&#xff…

【后端】.NET Core API框架搭建(8) --配置使用RabbitMQ

目录 1.添加包 2. 连接配置 2.1.连接字符串 2.2.连接对象 3.创建连接服务 3.1.添加配置获取方法 3.2.服务实现类 3.3.服务接口 4.创建生产者服务 4.1.生产者实现类 4.2.生产者接口 5.创建消费者服务 5.1.消费者服务接口 5.2.消费者接口 6.注册 7.简单使用案例 7.1.实现…

Apache SeaTunnel配置使用案例

前置操作 Apache SeaTunnel详解与部署&#xff08;最新版本2.3.11&#xff09;-CSDN博客 mkdir /usr/local/soft/apache-seatunnel-2.3.11/job/ 一、MySQL to HDFS 官方配置参考&#xff1a; MySQL | Apache SeaTunnel Hdfs文件 | Apache SeaTunnel 1、配置确认 将mysq…

GitCode 使用高频问题及解决方案

GitCode 作为一款强大的版本控制系统&#xff0c;在软件开发流程中起着举足轻重的作用。然而&#xff0c;在使用过程中&#xff0c;开发者们常常会遇到各种各样的问题。本文将汇总 GitCode 使用中的高频问题&#xff0c;并提供详细的解决方案&#xff0c;帮助开发者们更顺畅地使…

在FreeBSD系统使用chroot进入Ubuntu仿真环境使用Localsend软件发送和接受文件

LocalSend是一款非常实用的在不同系统&#xff08;Windows、MacOS、Linux、Android和IOS&#xff09;传递文件的程序。我们这次的实践&#xff0c;就是要在FreeBSD下也能发送和接收文件。 安装LocalSend 跟在Ubuntu下安装非常类似&#xff0c;只是不需要下面的第一步&#xf…

交叉熵损失F.cross_entropy在分类模型中的应用

一、核心思想&#xff1a;通过概率分布惩罚错误交叉熵损失的本质是&#xff1a; 比较模型预测的概率分布 vs 真实标签的概率分布&#xff0c;惩罚两者之间的差异。例如&#xff1a;真实标签&#xff1a;图像 0 → 文本 0&#xff08;独热编码 [1, 0, 0, ...]&#xff09;模型预…

测试学习之——Pytest Day3

引言Pytest 作为 Python 中最受欢迎的测试框架之一&#xff0c;以其简洁的语法、强大的功能和丰富的插件生态系统&#xff0c;极大地提升了自动化测试的效率和可维护性。在本文中&#xff0c;我们将深入探讨 Pytest 的两大核心特性&#xff1a;Fixture 和插件管理&#xff0c;帮…

控制Vue对话框显示隐藏

正确做法 — 使用 Vue 数据驱动控制显隐你不需要手动设置 display: block&#xff0c;因为 Element Plus 的 <el-dialog> 是基于 v-model 或 :visible.sync 控制的。&#x1f527; 修改模板部分&#xff1a;将原来的&#xff1a;<el-dialog title"报文详情"…

直播带货与开源AI智能名片链动2+1模式S2B2C商城小程序:重塑电商营销新格局

摘要&#xff1a;本文聚焦于直播带货对互联网供需关系的深刻影响&#xff0c;分析其如何改变传统电商营销模式&#xff0c;实现从“人找货”到“货找人”的转变。同时&#xff0c;引入开源AI智能名片链动21模式S2B2C商城小程序这一创新概念&#xff0c;探讨其在直播带货背景下的…

Jmeter 性能测试响应时间过长怎么办?

当 JMeter 性能测试中出现 响应时间过长 的问题时&#xff0c;需要从 测试脚本、服务器、网络、JMeter配置 等多方面排查和优化。以下是详细的解决步骤和思路&#xff1a; B站最新性能进阶&#xff0c;学会这些jmeter性能测试技能&#xff0c;更助于正确设计、执行和分析性能测…

COZE官方文档基础知识解读第三期 —— prompt(提示词)

COZE官方文档基础知识解读第三期 —— prompt&#xff08;提示词&#xff09; 对于初步接触PE&#xff08;prompt engineering&#xff09; 的小伙伴们&#xff0c;你们可以去火山方舟提供的prompt工具&#xff0c;用工具&#xff08;其余的prompt网站https://www.promptinggu…

代码随想录算法训练营第三十二天|动态规划理论基础、LeetCode 509. 斐波那契数、70. 爬楼梯、746. 使用最小花费爬楼梯

目录 LeetCode 509. 斐波那契数 70. 爬楼梯 746. 使用最小花费爬楼梯 感想 文档讲解&#xff1a;代码随想录 动态规划&#xff0c;英文&#xff1a;Dynamic Programming&#xff0c;简称DP&#xff0c;如果某一问题有很多重叠子问题&#xff0c;使用动态规划是最有效的。 …

SpringMVC3

一、JSON 与参数传递1.1JSON 是什么- JSON 是字符串&#xff1a;比如 {"name":"zhangsan","password":"123456","age":15} 就是一个 JSON 字符串&#xff0c;它用来在前后端、服务间传递数据。- JSON 库&#xff1a;Fastj…

查看.bin二进制文件的方式(HxD十六进制编辑器的安装)

文章目录Windows 系统上安装 HxD 十六进制编辑器的步骤。**HxD 是一款免费、轻量级的工具&#xff0c;适合查看和编辑 .bin 等二进制文件。****PS:实际安装过程中会发现找不到Windows11的版本&#xff0c;安装windows10的即可&#xff0c;并且没有区别setup版和portable版**安装…

Linux系统性能优化与监控

系统性能优化与监控是保障 Linux 服务器稳定运行的核心技术&#xff0c;涉及 ​​CPU、内存、磁盘 I/O、网络、进程​​ 等多维度的指标分析、问题定位与优化策略。以下从​​监控工具与指标​​、​​常见问题诊断​​、​​优化方法​​三个层面详细讲解&#xff0c;并结合​…

如何在 React + TypeScript 中实现 JSON 格式化功能

如何在 React TypeScript 中实现 JSON 格式化功能 作为前端开发者&#xff0c;我们经常需要处理 JSON 数据。无论是 API 调试、配置文件编辑还是数据转换&#xff0c;能够格式化 JSON 是一项基本但非常有用的技能。本文将详细介绍如何在 React 和 TypeScript 环境中实现 JSON…

Mac连接服务器Docker容器全攻略

苹果电脑( macOS 系统 )连接服务器、配置容器,整体思路和 Linux 终端操作更贴近,以下结合 macOS 特点,详细分步说明,以 Docker 容器 + 常见 Linux 服务器( 如 CentOS、Ubuntu )为例: 一、连接服务器(SSH 方式, macOS 终端原生支持 ) 1. 准备信息 找运维或云平台…

【字节跳动】数据挖掘面试题0019:带货直播间推荐:现在有一个带货的直播间,怎么把它精准地推送给有需要的用户

文章大纲 带货直播间推荐系统:原理、算法与实践 一、推荐系统在带货直播中的重要性 二、数据收集与处理 1. 用户数据 2. 直播间数据 3. 用户行为数据 4. 数据处理与特征工程 三、推荐算法实现 1. 基于内容的推荐 2. 基于协同过滤的推荐 3. 基于知识图谱的推荐 4. 混合推荐算法…