二叉树路径相关算法题|带权路径长度WPL|最长路径长度|直径长度|到叶节点路径|深度|到某节点的路径非递归(C)

带权路径长度WPL

二叉树的带权路径长度(WPL)是二叉树所有叶节点的带权路径长度之和,给定一棵二叉树T,采用二叉链表存储,节点结构为
在这里插入图片描述

其中叶节点的weight域保存该节点的非负权值,设root为指向T的根节点的指针,设计求WPL的算法

算法思想

![[Pasted image 20241120144005.png]]

方法1,树中全部叶节点的带权路径长度之和
采用递归思想
二叉树的WPL值 = 树中全部叶节点的带权路径长度之和 = 根节点左子树中全部叶节点的带权路径长度之和 + 根节点右子树中全部叶节点的带权路径长度之和
叶节点的带权路径长度 = 该节点的weight域的值 x 该节点的深度
设根节点的深度为0,若某节点的深度为d,其子节点的深度为d+1
递归过程中,如果遍历到叶节点,返回该节点的带权路径长度,否则返回其左右子树的带权路径长度之和

先遍历5,d是0,不是叶子节点,递归3和7,d为1,3不是叶子节点,7是叶子节点,返回1x7,再递归1和4,d为2,都是叶子节点,返回1x2和4x2,所以WPL为2+8+7=17

typedef struct node
{int weight;struct node *left, *right;
}BTNode;// 传入根节点,调用递归函数,初始深度d为0
int WPL(BTNode* root)
{return WPL1(root, 0);
}// 传入节点和该节点的深度
int WPL1(BTNode* root, int d)
{// 如果该节点是叶子节点,返回该叶子节点的weightxd,也就是带权路径长度if (root->left == NULL && root->right == NULL)return (root->weight * d);// 否则递归该节点的左孩子和右孩子,子节点的深度为根节点的深度+1elsereturn (WPL1(root->left, d + 1) + WPL1(root->right, d + 1));
}

WPL1是一个递归函数,用来计算从当前节点开始的WPL

最长路径长度

给定一个二叉树的root,返回最长的路径的长度,这个路径中的每个节点具有相同值。这条路径可以经过也可以不经过根节点。两个节点之间的路径的长度由它们之间的边数表示

算法思想

符合要求的左子树路径最大长度leftpath,符合要求的右子树路径最大长度rightpath,最后的路径长度为当前路径,与rightpath+leftpath最大值,注意全局变量的初始化位置
![[Pasted image 20241120204702.png]]

第一层
递归计算root的左右孩子的最长单向路径
右节点存在且与当前节点值相等,都是5
rightpath = right+1
![[Pasted image 20241120205655.png]]

第二层
right是右节点的DFS返回的值;
递归计算这个节点的左右孩子的最长单向路径,
同样右节点存在且与当前值相等,都是5
rightpath = right+1
![[Pasted image 20241120205727.png]]

第三层
right是右孩子返回的值,
递归左右孩子,为空,返回0
left和right都是0,path也是0,最后返回0
返回第二层
right是0,rightpath是1,path更新为1,返回1
返回第一层
right是1,rightpath是2,path更新为2,返回2

int path;   // 全局变量,用于存储当前发现的最长路径长度
// 返回两个正数中的较大值
int Max(int a, int b)
{return a >= b ? a : b;
}
// 递归地计算以root为根的子树中,以相同节点值构成的最长路径
int DFS(BTNode* root)
{// 如果当前节点为空,返回0,因为没有路径if (root == NULL)return 0;int left;int right;int leftpath = 0;int rightpath = 0;// 递归计算左子树和右子树的最长单向路径// left是左子树的最长路径,right是右子树的最长路径left = DFS(root->left);right = DFS(root->right);// 如果左节点存在且与当前节点值相等,最长路径+1if ((root->left) && (root->left->val == root->val))leftpath = left + 1;// 如果右节点存在且与当前节点值相等,最长路径+1if ((root->right) && (root->right->val == root->val))rightpath = right + 1;// leftpath+rightpath,当前节点处的最长路径的长度,经过当前节点// 更新pathpath = Max(path, leftpath + rightpath);// 返回从当前节点向下延伸的最长单项路径,用于供上层节点继续计算return Max(leftpath, rightpath);
}
int longestUnivaluePath(BTNode* root)
{if (root == NULL)return 0;path = 0;DFS(root);return path;
}

直径长度

给定一个二叉树,计算它的直径长度。一棵二叉树的直径长度是任意两个节点路径长度中的最大值。这条路径可能穿过也可能不穿过根节点

算法思想

递归求得节点的左子树的最大深度和右子树的最大深度,相加便是经过该节点的直径,遍历每个节点,更新最大值便可得到该二叉树的最大直径。

  1. 写出一个求树的最大深度的函数
  2. 递归调用这个函数,求得每个节点的左子树最大深度和右子树最大深度,相加

![[Pasted image 20241120211158.png]]

第一层
将rootA传入depth求深度,传入sum记录直径
递归计算左右子树B和C的深度
第二层
B继续递归左右子树的深度,D和F
C为叶子节点,没有左右孩子,left和right为0,new也就是直径为1,sum更新为1,返回深度为1
第三层
D为叶子节点,new为1,sum还是1,返回深度为1
F为叶子节点,new为1,sum还是1,返回深度为1
返回第二层
B,的left和right都是1,B的new也就是直径是3,sum更新为3,返回深度为2
返回第一层
A,left为2,right为1,new也就是直径为4,sum更新为4,返回深度为3
最后sum=4,返回sum-1 = 3

// 返回两个整数中的较大值
int max(int a, int b)
{return a > b ? a : b;
}// 递归计算二叉树的深度,同时更新全局变量记录直径
int depth(BTNode* root, int* sum)
{// 如果当前节点为空,返回深度为0if (root == NULL)return 0;// 左子树的最大深度int left = depth(root->left, sum);// 右子树的最大深度int right = depth(root->right, sum);// 经过根节点的直径,左子树最大深度与右子树最大深度之和int new = left + right + 1;// 更新sum,记录到目前为止的最大直径*sum = max(*sum, new);// 返回该节点深度的最大值,等于左右子树的最大深度+1return max(left, right) + 1;
}int dimeterBT(BTNode* root)
{if (root == NULL)return 0;int sum = 0;depth(root, &sum);// 返回sum-1,将节点数转换为边数作为直径的定义return sum - 1;
}

到叶节点路径

给你一个二叉树的根节点root,按任意顺序,返回所有从根节点到叶子节点的路径

算法思想

深度优先遍历二叉树,要考虑当前节点以及它的孩子节点,如果当前节点不是叶子节点,则在当前的路径末尾添加该节点,并继续递归遍历该节点的每一个孩子节点
如果当前节点是叶子节点,则在当前路径末尾添加该节点后就得到了从根节点到叶子节点的路径
![[Pasted image 20241121142757.png]]

将根节点的值1,入到stack深度下标的位置上,深度为0
![[Pasted image 20241121142855.png]]

有左右孩子,先递归左孩子,深度+1=1
2,入栈
![[Pasted image 20241121143227.png]]

有右孩子,递归右孩子,5入栈,depth为2
![[Pasted image 20241121143316.png]]

这时候左右孩子节点都为空
将1->2->5的路径保存

  • 动态分配一个字符串缓冲区tmp。用于存储当前路径的字符串
    1001是缓冲区的长度
  • len=0,记录路径字符串的当前长度,即字符串的写入位置,起始为0,表示字符串从头开始写入
  • 遍历当前路径中的所有节点值,即stack;使用sprintf追加到路径字符串tmp中
  • 添加叶节点值,在路径字符串的末尾,添加当前叶子节点的值
  • 将路径字符串存储到结果数组

返回1节点,递归右孩子3,深度为1,入栈,会覆盖掉2,变成3

// 定义二叉树节点结构 
typedef struct node 
{ int val; struct node* left; struct node* right; 
} BTNode; // 辅助函数:递归生成路径 
void generatePaths(BTNode* root, char** paths, int* returnSize, int* stack, int depth) 
{ if (root == NULL) return; // 将当前节点加入路径栈 stack[depth] = root->val; // 如果是叶子节点,生成路径字符串 if (root->left == NULL && root->right == NULL) { // 分配路径字符串 char* tmp = (char*)malloc(1001); int len = 0; // 构建路径字符串 for (int i = 0; i < depth; i++) { len += sprintf(tmp + len, "%d->", stack[i]); } // 添加叶子节点值 sprintf(tmp + len, "%d", root->val); // 将路径存储到结果数组 paths[(*returnSize)++] = tmp; return; } // 递归遍历左、右子树 generatePaths(root->left, paths, returnSize, stack, depth + 1); generatePaths(root->right, paths, returnSize, stack, depth + 1); 
} // 主函数:生成所有路径 
char** binaryTreePaths(BTNode* root, int* returnSize) 
{ // 分配结果数组 char** paths = (char**)malloc(sizeof(char*) * 1001); *returnSize = 0; // 路径栈 int stack[1001]; // 调用递归函数 generatePaths(root, paths, returnSize, stack, 0); return paths; 
}
  • binaryTreePaths,输入:root,二叉树的根节点,returnSize,用来返回路径数量
  • 返回paths,存储路径字符串的数组
  • paths是一个指针数组,用来存储每条路径的字符串
  • stack用来记录当前节点的路径值
  • generatePaths用来来递归生成路径

generatePaths

  • 如果节点root为NULL,直接返回,(递归退出条件)
  • 将当前节点的值存入栈stack[depth]
  • depth表示当前路径的深度
    处理叶子节点
  • 创建字符串,遍历路径栈stack,通过sprintf拼接路径值,将路径字符串的结果保存在paths中
  • 增加路径数量returnSize
    遍历左右子树
  • 如果当前节点不是叶子节点,遍历递归左子树和右子树
  • 子树深度为depth+1

深度

输入一棵二叉树的根节点,求该树的深度。从根节点到叶节点依次经过的节点形成树的一条路径,最长路径的长度为树的深度
9

算法思想

如果二叉树为空,深度为0,如果二叉树只有根节点,深度为1
如果二叉树的根节点只有左子树,深度为左子树的深度+1
如果二叉树的根节点只有右子树,深度为右子树的深度+1
如果二叉树的根节点既有既有左子树又有右子树,深度为左右子树深度最大者+1

int maxDepth(BTNode* root)
{if (root == NULL)return 0;// 当前节点的左子树的深度int Lenleft = maxDepth(root->left);// 当前节点的右子树的深度int Lenright = maxDepth(root->right);// 返回左右子树的深度的较大者+1return Lenleft > Lenright ? Lenleft+1 : Lenright+1;
}

到某节点的路径非递归

假设二叉树采用链式存储结构进行存储,root为根节点,p为任意给定节点
求从根节点到p之间路径的非递归算法

算法思想

采用后序非递归遍历二叉树,栈中保留从根节点到当前节点的路径上的所有节点
![[Pasted image 20241121145246.png]]

  • cur是当前遍历的节点
  • s是栈,用来存储当前节点及其祖先节点,用于后续回溯
  • tag数组用于标记每个节点的左右子树是否已经被访问
  • top用来表示栈的当前高度
    while循环用于实现二叉树的深度优先遍历,只要当前节点不为空或者栈不为空就继续
    while循环表示从当前cur节点开始,一直往左遍历,直到没有左子树为止
    每次访问一个节点,将该节点入栈,并设置tag为0,表示其右子树未被访问
    如果当前节点cur就是p,就打印从根节点到p的路径,通过s栈表示

由于p是3节点,将1,2,4,入栈
![[Pasted image 20241121150425.png]]

处理右子树,检查tap里的数据,如果右子树被访问过,就弹出该节点,跳过所有已经完全访问过的节点
如果该节点的右子树未被访问,就会进入右子树进行遍历,

遍历4节点的右子树,tag置为1,节点为空,直接返回
![[Pasted image 20241121150742.png]]

这是循环过来,4被访问过,出栈
![[Pasted image 20241121150805.png]]

再遍历2的右节点,
![[Pasted image 20241121150827.png]]

// 查找从根节点到指定节点p的路径
void Path(BTNode* root, BTNode* p)
{if (root == NULL || p == NULL)return;BTNode* cur = root;// 存储节点路径的栈int s[MaxSize];// 栈的当前高度int top = 0;// 标记左右子树是否访问过int tag[MaxSize] = {0};while (cur || top > 0){// 向左走直到最左叶子节点while (cur){// 将当前节点入栈s[++top] = cur;// 标记为未访问右子树tag[top] = 0;// 找到目标节点pif (cur == p){printf("从根节点到p节点的路径为\n");for (int i = 1; i <= top; i++){printf("%d", s[i]->data);if (i < top)printf("->");printf("\n");return;}}// 继续遍历左子树cur = cur->left;}// 如果当前节点的左子树遍历完,回溯到父节点while (top > 0 && tag[top] == 1)// 出栈top--;// 访问右子树if (top > 0){// 访问当前节点的右子树cur = s[top];// 进入右子树cur = cur->right;// 标记右子树已访问tag[top] = 1;}}
}

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

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

相关文章

Ubuntu ESP32开发环境搭建

文章目录 ESP32开发环境搭建安装ESP-IDF搭建一个最小工程现象 ESP32开发环境搭建 最近有个小项目需要用到能够联网的mcu驱动&#xff0c;准备玩玩esp的芯片&#xff0c;记录下ESP32开发环境搭建的过程。 ESP-IDF 是乐鑫科技为其 ESP32 系列芯片提供的官方开发框架。这个框架主…

2024.5 AAAiGLaM:通过邻域分区和生成子图编码对领域知识图谱对齐的大型语言模型进行微调

GLaM: Fine-Tuning Large Language Models for Domain Knowledge Graph Alignment via Neighborhood Partitioning and Generative Subgraph Encoding 问题 如何将特定领域知识图谱直接整合进大语言模型&#xff08;LLM&#xff09;的表示中&#xff0c;以提高其在图数据上自…

《SpringBoot、Vue 组装exe与套壳保姆级教学》

&#x1f4e2; 大家好&#xff0c;我是 【战神刘玉栋】&#xff0c;有10多年的研发经验&#xff0c;致力于前后端技术栈的知识沉淀和传播。 &#x1f497; &#x1f33b; CSDN入驻不久&#xff0c;希望大家多多支持&#xff0c;后续会继续提升文章质量&#xff0c;绝不滥竽充数…

django宠物服务管理系统

摘 要 宠物服务管理系统是一种专门为宠物主人和宠物服务提供商设计的软件。它可以帮助用户快速找到附近的宠物医院、宠物美容店、宠物寄养中心等服务提供商&#xff0c;并预订相关服务。该系统还提供了一系列实用的功能。通过使用宠物服务管理系统&#xff0c;用户可以更加方便…

docker 容器运行Ruoyi-cloud

1&#xff0c;linux系统安装openjdk1.8,mvn,dokcer,node,git 2&#xff0c;拉取代码 1&#xff09;查看gitee仓库地址 2&#xff09;创建/app文件夹&#xff0c;进入app目录 mkdir /app cd /app 3&#xff09;clone代码 4&#xff09;修改配置文件中nacos地址 # 修改注…

Linux运维篇-iscsi存储搭建

目录 概念实验介绍环境准备存储端软件安装使用targetcli来管理iSCSI共享存储 客户端软件安装连接存储 概念 iSCSI是一种在Internet协议上&#xff0c;特别是以太网上进行数据块传输的标准&#xff0c;它是一种基于IP Storage理论的存储技术&#xff0c;该技术是将存储行业广泛…

《Spring 数据访问:高效整合数据库与 ORM》

一、Spring 数据访问概述 Spring 在数据访问方面具有至关重要的地位&#xff0c;它为开发者提供了强大而高效的数据访问解决方案。 &#xff08;一&#xff09;强大的数据访问支持 Spring 提供了多种数据访问方式&#xff0c;以满足不同项目的需求。JDBC 是一种传统的数据访问…

AMD(Xilinx) FPGA配置Flash大小选择

目录 1 FPGA配置Flash大小的决定因素2 为什么选择的Flash容量大小为最小保证能够完成整个FPGA的配置呢&#xff1f; 1 FPGA配置Flash大小的决定因素 在进行FPGA硬件设计时&#xff0c;选择合适的配置Flash是我们进行硬件设计必须考虑的&#xff0c;那么配置Flash大小的选择由什…

解读缓存问题的技术旅程

目录 前言1. 问题的突发与初步猜测2. 缓存的“隐身术”3. 缓存策略的深层优化4. 反思与感悟结语 前言 那是一个普通的工作日&#xff0c;团队例行的早会刚刚结束&#xff0c;我正准备继续优化手头的模块时&#xff0c;突然收到了用户反馈。反馈的内容是部分数据显示异常&#…

Block Successive Upper Bound Minimization Method(BSUM)算法

BSUM优化方法学习 先验知识参考资料1 A Unified Convergence Analysis of Block Successive Minimization Methods for Nonsmooth OptimizationSUCCESSIVE UPPER-BOUND MINIMIZATION (SUM) 连续上限最小化算法THE BLOCK SUCCESSIVE UPPER-BOUND MINIMIZATION ALGORITHM 块连续上…

开源 AI 智能名片 2+1 链动模式商城小程序:场景驱动的商业创新与用户价值挖掘

摘要&#xff1a;本文围绕开源 AI 智能名片 21 链动模式商城小程序源码&#xff0c;深入分析了场景中的时间、空间、设备、社交和状态五大核心元素。阐述了各元素的表现形式、应用策略及价值&#xff0c;包括时间元素对业务周期和用户行为的影响及相应营销策略&#xff1b;空间…

【PyTorch】Pytorch中torch.nn.Conv1d函数详解

1. 函数定义 torch.nn.Conv1d 是 PyTorch 中用于一维卷积操作的类。定义如下&#xff1a; 官方文档&#xff1a;https://pytorch.ac.cn/docs/stable/generated/torch.nn.Conv1d.html#torch.nn.Conv1d torch.nn.Conv1d(in_channels, out_channels, kernel_size, stride1,paddi…

绿光一字线激光模组:工业制造与科技创新的得力助手

在现代工业制造和科技创新领域&#xff0c;绿光一字线激光模组以其独特的性能和广泛的应用前景&#xff0c;成为了不可或缺的关键设备。这种激光模组能够发射出一条明亮且精确的绿色激光线&#xff0c;具有高精度、高稳定性和长寿命的特点&#xff0c;为各种精密加工和测量需求…

【Linux】【Shell】Shell 基础与变量

Shell 基础 Shell 基础查看可用的 Shell判断当前 Shell 类型 变量环境变量查看环境变量临时环境变量永久环境变量PATH 变量 自定义变量特殊赋值(双引号、单引号、反撇号) 预定义变量bashrc Shell 基础 Shell 是一个用 C 语言编写的程序&#xff0c;相当于是一个翻译&#xff0c…

【SQL50】day 2

目录 1.每位经理的下属员工数量 2.员工的直属部门 3.判断三角形 4.上级经理已离职的公司员工 5.换座位 6.电影评分 7.修复表中的名字 8.患某种疾病的患者 9.删除重复的电子邮箱 1.每位经理的下属员工数量 # Write your MySQL query statement below #e1是经理&#xff0c;…

FIFO和LRU算法实现操作系统中主存管理

FIFO&#xff0c;用数组实现 1和2都是使用nextReplace实现新页面位置的更新 1、不精确时间&#xff1a;用ctime输出运行时间都是0.00秒 #include <iostream> #include <iomanip> #include<ctime>//用于计算时间 using namespace std;// 页访问顺序 int pa…

ssm161基于web的资源共享平台的共享与开发+jsp(论文+源码)_kaic

毕 业 设 计&#xff08;论 文&#xff09; 题目&#xff1a;资源共享平台设计与实现 摘 要 现代经济快节奏发展以及不断完善升级的信息化技术&#xff0c;让传统数据信息的管理升级为软件存储&#xff0c;归纳&#xff0c;集中处理数据信息的管理方式。本资源共享平台就是在…

LeetCode 力扣 热题 100道(六)合并两个有序链表(C++)

合并两个有序链表 将两个升序链表合并为一个新的 升序 链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。 输入&#xff1a;l1 [1,2,4], l2 [1,3,4] 输出&#xff1a;[1,1,2,3,4,4]示例 2&#xff1a; 输入&#xff1a;l1 [], l2 [] 输出&#xff1a;[]示…

ubuntu显示管理器_显示导航栏

ubuntu文件管理器_显示导航栏 一、原始状态&#xff1a; 二、显示导航栏状态&#xff1a; 三、原始状态--->导航栏状态: 1、打开dconf编辑器&#xff0c;直接在搜索栏搜索 dconf-editor ------如果没有安装&#xff0c;直接按流程安装即可。 2、进入目录&#xff1a;org …

高集成的MCU方案已成电机应用趋势?

【哔哥哔特导读】高集成化的芯片成为当下MCU领域研发和市场布局的重点&#xff0c;但是在实际应用中仍然面临散热等痛点问题&#xff0c;MCU厂商是如何解决和优化这些痛点&#xff1f; 随着全球工业自动化、智能制造和绿色发展的不断推进&#xff0c;中国电机行业正站在新一轮…