Deprecated: imwpcache\f884414bce24ee67f\f73723ec7b1919fa5::__construct(): Implicitly marking parameter $YECBGYFECGEAFWHA as nullable is deprecated, the explicit nullable type must be used instead in /www/wwwroot/www.chuangxiangniao.com/wp-content/plugins/imwpcache-dist/build/f884414bce24ee67ff73723ec7b1919fa5.php on line 2

Deprecated: imwpcache\f884414bce24ee67f\f73723ec7b1919fa5::__construct(): Implicitly marking parameter $BBWFDDBHHYHDXXAB as nullable is deprecated, the explicit nullable type must be used instead in /www/wwwroot/www.chuangxiangniao.com/wp-content/plugins/imwpcache-dist/build/f884414bce24ee67ff73723ec7b1919fa5.php on line 2
递归更新树形结构中指定节点及其父节点的数值(排除根节点)_创想鸟

递归更新树形结构中指定节点及其父节点的数值(排除根节点)

递归更新树形结构中指定节点及其父节点的数值(排除根节点)

本文介绍如何在JavaScript中,针对一个嵌套的树形数据结构,根据指定的唯一键值,递归地更新匹配节点及其所有祖先节点的 curr 属性,同时确保顶层(根)节点不被修改。通过一个带有深度参数和布尔返回值传播机制的递归函数,实现精确控制更新范围。

问题概述

在处理具有层级关系的树形数据时,我们经常需要根据某个特定节点的标识(例如 key)来更新其自身以及其所有父级节点的信息。一个典型的场景是,当子节点发生某个事件时,其父级节点也需要相应地汇总或更新状态。然而,在某些情况下,我们可能希望更新操作只影响到指定节点及其上级节点,而排除最顶层的根节点。

考虑以下这种嵌套的JavaScript对象数组结构:

const data = [  {    key: "id1",    name: "Category 1",    curr: 0,    total: 0,    nodes: [      {        key: "id2",        name: "Applications",        curr: 20,        total: 30,        nodes: [          {            key: "id3",            name: "Gaming",            curr: 5,            total: 10,            nodes: []          },          {            key: "id4",            name: "Operating System",            curr: 15,            total: 20,            nodes: []          }        ]      }    ]  },  {    key: "id5",    name: "Category 2",    curr: 0,    total: 0,    nodes: [      {        key: "id6",        name: "Sub Category",        curr: 12,        total: 48,        nodes: [          {            key: "id7",            name: "Inside Sub",            curr: 12,            total: 48,            nodes: []          }        ]      }    ]  },  {    key: "id8",    name: "Last One",    curr: 0,    total: 0,    nodes: []  }];

每个节点都包含一个唯一的 key、name、curr(当前值)、total(总值)以及一个 nodes 数组,表示其子节点。我们的目标是,当传入一个 key 时,找到匹配的节点,将其 curr 值递增,并且将其所有父节点的 curr 值也递增,但不递增最顶层(level 0)的节点。

例如,如果调用 incrementRecursively(data, “id4”),预期的输出是 id4 的 curr 从 15 变为 16,其父节点 id2 的 curr 从 20 变为 21。而 id1(level 0)的 curr 保持不变。

核心挑战与传统方法局限

直接的递归遍历通常只能在找到目标节点时进行操作,但无法直接通知其父节点进行更新。如果父节点需要根据子节点的状态进行更新,传统的 forEach 遍历或简单的递归函数难以实现这种“自下而上”的更新传播。此外,如何精确控制更新的层级(即排除根节点)也是一个需要巧妙处理的问题。

解决方案:带深度控制的递归更新

解决此类问题的关键在于利用递归函数的返回值来向上级传递状态信息,并引入一个深度参数来控制更新的边界。

算法思路:

定义一个递归函数,该函数接收当前节点数组、目标 key 和当前递归深度 depth。遍历当前节点数组中的每个节点。对于每个节点,检查其 key 是否与目标 key 匹配。如果 key 匹配,或者对当前节点的子节点进行递归调用后返回 true(表示子节点中发生了更新),则说明当前节点是目标节点或其祖先节点。在这种情况下,判断当前 depth 是否大于 0。如果 depth > 0,则说明当前节点不是根节点,可以安全地递增其 curr 值。无论是否递增了 curr,都向上级返回 true,表示其子树中发生了更新。如果遍历完当前层级的所有节点,都没有找到匹配项或子节点未更新,则返回 false。

代码实现:

/** * 递归更新树形结构中指定节点及其父节点的curr值,排除根节点。 * @param {Array} nodes - 当前层级的节点数组。 * @param {string} key - 要查找并更新的目标节点的key。 * @param {number} depth - 当前节点的深度,根节点为0。 * @returns {boolean} - 如果当前节点或其子节点中发生了更新,则返回true;否则返回false。 */function inc(nodes, key, depth = 0) {    // 遍历当前层级的每一个节点    for (let n of nodes) {        // 检查当前节点的key是否匹配,或者其子节点(如果存在)中是否发生了更新        // n.nodes ?? [] 用于处理nodes可能为null或undefined的情况        if (n.key === key || inc(n.nodes ?? [], key, depth + 1)) {            // 如果当前节点是匹配节点或其祖先节点,并且不是最顶层的根节点(depth > 0)            if (depth > 0) {                n.curr++; // 递增当前节点的curr值            }            // 返回true,向上层调用者表明在其子树中发生了更新            return true;        }    }    // 遍历完当前层级,没有找到匹配项,也没有子节点更新    return false;}

代码解析:

inc(nodes, key, depth = 0): 函数接收三个参数:当前层级的 nodes 数组,目标 key,以及一个 depth 参数,默认为 0,表示最顶层。for (let n of nodes): 迭代当前 nodes 数组中的每个节点。n.key === key || inc(n.nodes ?? [], key, depth + 1): 这是核心的判断逻辑。n.key === key: 检查当前节点是否是我们要找的目标节点。inc(n.nodes ?? [], key, depth + 1): 递归调用自身,处理当前节点的子节点。depth + 1 确保了深度参数正确递增。n.nodes ?? [] 使用空合并运算符,以防 n.nodes 为 null 或 undefined,确保递归调用时始终传入一个数组。|| 运算符:如果当前节点匹配,或者其任何子节点(或子节点的子节点)发生了更新(递归调用返回 true),则整个条件为真。这意味着当前节点是目标节点本身,或者它是目标节点的某个祖先节点。if (depth > 0): 这是实现“排除根节点”的关键。只有当当前节点的深度大于 0 时(即它不是最顶层的节点),才允许递增其 curr 值。n.curr++: 递增当前节点的 curr 值。return true: 如果在当前节点或其子树中找到了目标 key 并进行了更新,则返回 true。这个 true 值会传递给上一层递归调用,使其父节点也能识别到子树中发生了更新,从而决定是否递增自身的 curr 值。return false: 如果遍历完当前层级的所有节点,都没有找到匹配的 key,并且其子树中也没有发生更新,则返回 false。

使用示例

让我们使用上述 inc 函数来更新我们的 data 结构。

const data = [  {    key: "id1",    name: "Category 1",    curr: 0,    total: 0,    nodes: [      {        key: "id2",        name: "Applications",        curr: 20,        total: 30,        nodes: [          {            key: "id3",            name: "Gaming",            curr: 5,            total: 10,            nodes: []          },          {            key: "id4",            name: "Operating System",            curr: 15,            total: 20,            nodes: []          }        ]      }    ]  },  {    key: "id5",    name: "Category 2",    curr: 0,    total: 0,    nodes: [      {        key: "id6",        name: "Sub Category",        curr: 12,        total: 48,        nodes: [          {            key: "id7",            name: "Inside Sub",            curr: 12,            total: 48,            nodes: []          }        ]      }    ]  },  {    key: "id8",    name: "Last One",    curr: 0,    total: 0,    nodes: []  }];console.log("原始数据:", JSON.stringify(data, null, 2));// 示例1: 更新 'id4'inc(data, 'id4');console.log("n更新 'id4' 后的数据:", JSON.stringify(data, null, 2));/* 预期输出:  id4.curr: 15 -> 16  id2.curr: 20 -> 21  id1.curr: 0 (不变,因为 depth > 0 限制)*/// 示例2: 更新 'id7'inc(data, 'id7');console.log("n更新 'id7' 后的数据:", JSON.stringify(data, null, 2));/* 预期输出:  id7.curr: 12 -> 13  id6.curr: 12 -> 13  id5.curr: 0 (不变)*/// 示例3: 尝试更新一个不存在的keyinc(data, 'nonExistentKey');console.log("n尝试更新不存在的key后的数据 (无变化):", JSON.stringify(data, null, 2));

注意事项

数据变动性: 该 inc 函数会直接修改传入的 data 数组。如果需要保持原始数据的不可变性,应在函数内部对相关节点进行深拷贝,并返回一个新的数据结构。例如,可以使用 map 结合深拷贝来创建新的节点。键的唯一性: 解决方案依赖于 key 属性在整个树中的唯一性。如果 key 不唯一,可能会导致意外的更新。性能考量: 对于非常深或节点数量庞大的树,递归深度可能成为问题。但通常JavaScript引擎对递归深度有优化,对于一般应用场景是足够的。极端情况下,可以考虑将递归转换为迭代(例如使用栈)。错误处理: 如果传入的 key 不存在于树中,inc 函数将不会执行任何更新,并最终返回 false。可以根据需要添加额外的日志或错误抛出机制。depth 参数的重要性: depth 参数是精确控制更新范围的关键。通过将其初始化为 0 并随着递归递增,我们能够区分根节点(depth === 0)和非根节点(depth > 0),从而实现有条件的更新。

总结

通过结合递归函数的返回值来传递更新状态和深度参数来控制更新范围,我们能够高效且精确地解决在树形结构中,根据指定键值递归更新节点及其祖先节点,同时排除根节点的问题。这种模式在处理复杂嵌套数据结构时非常有用,为树形数据的状态管理提供了一个强大的工具。理解并灵活运用这种递归策略,可以有效简化许多树形数据操作的逻辑。

以上就是递归更新树形结构中指定节点及其父节点的数值(排除根节点)的详细内容,更多请关注创想鸟其它相关文章!

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 chuangxiangniao@163.com 举报,一经查实,本站将立刻删除。
发布者:程序猿,转转请注明出处:https://www.chuangxiangniao.com/p/1520251.html

赞 (0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
JavaScript字符串分割进阶:利用正则表达式巧妙处理带引号的字段
上一篇 2025年12月20日 13:09:41
TypeScript 动态导入命名空间成员的类型安全访问实践
下一篇 2025年12月20日 13:09:49

相关推荐

  • 夸克Ai搜索如何设置默认_夸克Ai搜索默认引擎更改

    首先在夸克APP中将默认搜索引擎设为AI引擎,再开启相关AI功能开关以启用AI搜索服务。具体步骤:1、打开夸克APP,点击右下角菜单进入设置;2、选择“通用”选项,点击“搜索引擎”;3、选择“AI引擎”或“夸克AI搜索”作为默认服务;4、返回主界面测试搜索关键词,确认AI结果是否展示;5、进入“AI…

    2026年9月21日
    400
  • Laravel中的Blade模板引擎基础用法

    blade模板引擎在laravel中用于简化视图开发。具体使用方法如下:1.输出变量:{{ $variable }}。2.条件判断:@if、@else、@elseif。3.循环:@foreach。4.模板继承:@extends、@section、@yield。blade让视图代码更简洁易读,但需注意…

    2026年9月21日
    000
  • Windows10重置此电脑卡住不动了怎么办_Windows10重置电脑卡住修复方法

    重置电脑卡住时,先等待2-4小时观察硬盘灯是否闪烁,确认系统是否仍在运行;若无响应,可尝试断开网络避免更新下载、调整BIOS关闭Secure Boot并启用Legacy模式;或使用Windows安装U盘启动,进入修复模式执行启动修复、chkdsk磁盘检查,以及通过三次强制关机触发恢复环境重试重置。 …

    2026年9月21日
    000
  • iPhone 17 Pro如何关闭后台应用刷新

    关闭iPhone后台应用刷新可省电省流量,进入设置→通用→后台App刷新,关闭顶部总开关或单独关闭特定App,还能提升系统流畅度。 虽然目前还没有iPhone 17 Pro,但关闭后台应用刷新的方法在所有iPhone上都是一样的。你可以通过设置里的“通用”选项来管理这个功能,既能省电也能减少数据使用…

    2026年9月21日
    100
  • Linux查看系统日志的常用命令

    答案是查看Linux日志需综合使用journalctl、dmesg、tail、grep等工具。journalctl用于systemd系统集中查询服务及内核日志,支持时间、优先级、字段等多维度过滤;dmesg专注内核启动与硬件问题;tail -f实时监控日志动态;cat、grep、less结合正则和管…

    用户投稿 2026年9月21日
    000
  • Java中设计可扩展类的技巧与经验

    设计可扩展类应优先组合而非继承,通过接口解耦;明确开放protected扩展点并封闭关键逻辑;提供详细文档说明扩展规则;谨慎处理状态与初始化,避免构造器中调用可重写方法;多数场景推荐接口与组合,必要时才允许继承。 在Java中设计可扩展类时,核心目标是让类既能满足当前需求,又便于未来被安全、可控地继…

    2026年9月21日
    100
  • mysql如何实现后台管理系统

    答案:基于MySQL的%ignore_a_1%需设计用户、权限、日志等表结构,通过后端语言实现安全的CRUD接口与JWT认证,前端展示数据并控制权限,确保系统安全稳定。 实现一个基于 MySQL 的后台管理系统,核心是构建一个安全、稳定、可扩展的系统架构,将数据库作为数据存储层,配合后端语言和前端界…

    2026年9月21日
    000
  • Workerman服务启动失败的排查步骤

    workerman服务启动失败的排查步骤如下:1. 检查配置文件,确保无语法错误;2. 查看系统日志,寻找错误线索;3. 检查端口占用情况,确保端口未被占用;4. 调整文件权限,确保workerman有足够权限;5. 检查php环境,确保版本兼容且扩展已安装。 关于Workerman服务启动失败的排…

    2026年9月21日
    200
  • 如何为VSCode设置自定义的代码高亮颜色?

    答案:通过settings.json中的editor.tokenColorCustomizations可自定义VSCode代码高亮颜色,支持全局或特定主题下修改关键字、字符串等元素颜色,结合textMateRules和作用域精确控制,提升代码可读性。 为 VSCode 设置自定义的代码高亮颜色,可以…

    2026年9月21日
    000
  • 百度浏览器自动跳转怎么办 百度浏览器页面跳转广告拦截方法

    百度浏览器自动跳转通常由恶意软件或设置被篡改引起,需检查浏览器设置、清除异常插件、修复快捷方式与注册表,并使用安全软件扫描清理,同时启用广告拦截与隐私保护功能以彻底解决问题。 百度浏览器出现自动跳转,通常不是浏览器本身的问题,而是由恶意软件、插件或设置被篡改导致的。解决这个问题需要从多个方面入手,检…

    2026年9月21日
    100
  • 115网盘资源查找入口_115网盘资源快速链接通道

    115网盘资源查找入口为http://www.115.com/,支持多平台访问、高效媒体管理及安全存储,提供网页端与客户端多种使用方式。 115网盘资源查找入口在哪里?这是不少网友都关注的,接下来由PHP小编为大家带来115网盘资源快速链接通道,感兴趣的网友一起随小编来瞧瞧吧! http://www…

    2026年9月21日
    000
  • OPPO A2 Pro充电提示音太响怎么关 OPPO A2 Pro系统音量管理

    关闭充电提示音最简单:进入设置→声音与振动→系统反馈→关闭充电提示音;若通过Breeno设置了自动指令,需在小布指令中删除相关规则;也可调低系统反馈中的充电提示音量以降低响度。 OPPO A2 Pro充电提示音太响,可以通过关闭系统中的充电提示音功能来解决。这个声音属于系统反馈音效,并非应用通知,所…

    2026年9月21日
    400
  • 压力测试(Benchmark)Swoole服务的工具与方法

    进行swoole服务的压力测试是为了确保服务在高负载下稳定运行。1. 选择工具:apache jmeter、wrk、locust。2. 使用方法:jmeter通过脚本配置,wrk通过命令行,locust通过python脚本。3. 注意事项:环境隔离、数据监控、脚本设计。4. 优化点:内存泄漏、连接池…

    2026年9月21日
    000
  • Windows11内存占用率过高怎么解决_Windows11内存占用过高修复方法

    1、通过任务管理器结束高内存占用进程;2、禁用Superfetch(SysMain)服务以降低内存负担;3、优化启动项减少后台负载;4、升级物理内存条提升系统性能。 如果您发现Windows 11系统运行缓慢,并且任务管理器显示内存占用率持续处于高位,这可能是由于后台进程过多、系统服务占用资源或硬件…

    2026年9月21日
    100
  • mysql常用存储引擎有哪些

    InnoDB是现代MySQL应用的首选存储引擎,因其支持事务(ACID)、行级锁、外键约束、崩溃恢复和MVCC,适用于高并发、数据完整性要求高的OLTP场景;MyISAM虽读取快但仅支持表级锁且无事务和外键,适用于读多写少的简单场景,已逐渐被淘汰;Memory引擎将数据存于内存,速度快但易失,适合临…

    2026年9月21日
    000
  • iPhone 12 Pro Max如何启用防水提示

    iPhone 12 Pro Max 具备 IP68 级防水,依赖密封设计无需启用;进水时会提示“闪电符号”,需晾干并避免使用吹风机,防水性能随时间可能下降。 iPhone 12 Pro Max 没有需要“启用”的防水提示功能。它的防溅、抗水和防尘能力是出厂时的硬件设计特性,无法通过设置开关来开启或关…

    2026年9月21日
    100
  • Linux目录结构与Windows目录结构对比

    Linux采用单一树状结构,所有文件系统挂载于根目录/下,如/home、/etc;Windows以C:\、D:\等独立盘符划分,无统一根节点。2. Linux将配置集中于/etc,用户数据存于/home,系统文件在/bin、/usr等,配置明文可编辑;Windows程序装在Program Files…

    用户投稿 2026年9月21日
    100
  • 在Java中多态是如何通过虚方法实现的

    多态通过动态方法调度实现,JVM利用虚方法表(vtable)在运行时根据对象实际类型确定方法调用。Java中除private、static、final方法和构造器外均为虚方法,子类重写方法后其vtable指向新实现,调用时JVM通过对象类型查找vtable定位具体方法。如Animal a = new…

    2026年9月21日
    000
  • 谷歌浏览器图片无法显示怎么办 谷歌浏览器图片加载失败修复方法

    首先检查浏览器图片显示设置是否允许,确认无误后清除缓存和Cookie数据,接着排查扩展程序干扰,最后更新浏览器并检查硬件加速设置。 谷歌浏览器图片加载不出来,通常不是大问题,多数情况通过几个简单操作就能解决。下面列出几种常见且有效的排查方法。 检查图片显示设置 最直接的原因可能是浏览器被设置为阻止图…

    2026年9月21日
    000
  • 如何配置VSCode来完美支持Vue.js开发?

    安装Volar、TypeScript Vue Plugin、ESLint和Prettier扩展,禁用Vetur,在settings.json中配置vetur.enabled为false,设置ESLint保存时自动修复并指定Prettier为默认格式化工具,关联.vue文件语言,启用TypeScrip…

    2026年9月21日
    000

发表回复

登录后才能评论
关注微信