当前位置: 首页 > news >正文

【深度优先搜索】【树】【C++算法】2003. 每棵子树内缺失的最小基因值

作者推荐

动态规划的时间复杂度优化

本文涉及知识点

深度优先搜索

LeetCode2003. 每棵子树内缺失的最小基因值

有一棵根节点为 0 的 家族树 ,总共包含 n 个节点,节点编号为 0 到 n - 1 。给你一个下标从 0 开始的整数数组 parents ,其中 parents[i] 是节点 i 的父节点。由于节点 0 是 根 ,所以 parents[0] == -1 。
总共有 105 个基因值,每个基因值都用 闭区间 [1, 105] 中的一个整数表示。给你一个下标从 0 开始的整数数组 nums ,其中 nums[i] 是节点 i 的基因值,且基因值 互不相同 。
请你返回一个数组 ans ,长度为 n ,其中 ans[i] 是以节点 i 为根的子树内 缺失 的 最小 基因值。
节点 x 为根的 子树 包含节点 x 和它所有的 后代 节点。
示例 1:
在这里插入图片描述

输入:parents = [-1,0,0,2], nums = [1,2,3,4]
输出:[5,1,1,1]
解释:每个子树答案计算结果如下:

  • 0:子树包含节点 [0,1,2,3] ,基因值分别为 [1,2,3,4] 。5 是缺失的最小基因值。
  • 1:子树只包含节点 1 ,基因值为 2 。1 是缺失的最小基因值。
  • 2:子树包含节点 [2,3] ,基因值分别为 [3,4] 。1 是缺失的最小基因值。
  • 3:子树只包含节点 3 ,基因值为 4 。1是缺失的最小基因值。
    示例 2:
    在这里插入图片描述

输入:parents = [-1,0,1,0,3,3], nums = [5,4,6,2,1,3]
输出:[7,1,1,4,2,1]
解释:每个子树答案计算结果如下:

  • 0:子树内包含节点 [0,1,2,3,4,5] ,基因值分别为 [5,4,6,2,1,3] 。7 是缺失的最小基因值。
  • 1:子树内包含节点 [1,2] ,基因值分别为 [4,6] 。 1 是缺失的最小基因值。
  • 2:子树内只包含节点 2 ,基因值为 6 。1 是缺失的最小基因值。
  • 3:子树内包含节点 [3,4,5] ,基因值分别为 [2,1,3] 。4 是缺失的最小基因值。
  • 4:子树内只包含节点 4 ,基因值为 1 。2 是缺失的最小基因值。
  • 5:子树内只包含节点 5 ,基因值为 3 。1 是缺失的最小基因值。
    示例 3:

输入:parents = [-1,2,3,0,2,4,1], nums = [2,3,4,5,6,7,8]
输出:[1,1,1,1,1,1,1]
解释:所有子树都缺失基因值 1 。

提示:
n == parents.length == nums.length
2 <= n <= 105
对于 i != 0 ,满足 0 <= parents[i] <= n - 1
parents[0] == -1
parents 表示一棵合法的树。
1 <= nums[i] <= 105
nums[i] 互不相同。

深度优先搜索

除了基因1的节点及它的祖先,其它节点都缺少1。
DFS(cur)结束时,处理了且只处理了它哥哥及自己的后代,如果我们将基因1及其祖先调整成长子。可以将空间复杂从O(nlogn)降低到O(n)。
注意:如果不优化,空间复杂度是O(nn),就是直接为每个节点分配空间,复制所有的后代。极端情况下,独子树的空间复杂度是O(nn)。直接用子树的空间,独子树空间复杂度O(n);非独子树O(nlong)。

超时代码

class CParentToNeiBo
{
public:CParentToNeiBo(const vector<int>& parents){m_vNeiBo.resize(parents.size());for (int i = 0; i < parents.size(); i++){if (-1 == parents[i]){m_root = i;}else{m_vNeiBo[parents[i]].emplace_back(i);}}}vector<vector<int>> m_vNeiBo;int m_root=-1;
};class Solution {
public:vector<int> smallestMissingValueSubtree(vector<int>& parents, vector<int>& nums) {CParentToNeiBo neiBo(parents);m_nums = nums;m_vIs1.resize(nums.size());m_ans.assign(nums.size(),1);m_vHas.resize(100'000+10);DFS1(neiBo.m_root, neiBo.m_vNeiBo);for (auto& v : neiBo.m_vNeiBo){for (int j = 1; j < v.size(); j++){if (m_vIs1[v[j]]){std::swap(v[0], v[j]);}}}DFS2(neiBo.m_root, neiBo.m_vNeiBo);return m_ans;}void DFS2(int cur, vector<vector<int>>& neiBo){		for (const auto& next : neiBo[cur]){DFS2(next, neiBo);}m_vHas[m_nums[cur]] = true;while (m_vHas[m_iNeed]){m_iNeed++;}if (m_vIs1[cur]){m_ans[cur] = m_iNeed;}}bool DFS1(int cur, vector<vector<int>>& neiBo){bool b = (1 == m_nums[cur]);		for (const auto& next : neiBo[cur]){b |= DFS1(next, neiBo);}return m_vIs1[cur]=b;}vector<int> m_nums,m_ans;vector<bool> m_vIs1;int m_iNeed = 1;vector<bool> m_vHas;
};

1及其祖先不用DFS

class CParentToNeiBo
{
public:CParentToNeiBo(const vector<int>& parents){m_vNeiBo.resize(parents.size());for (int i = 0; i < parents.size(); i++){if (-1 == parents[i]){m_root = i;}else{m_vNeiBo[parents[i]].emplace_back(i);}}}vector<vector<int>> m_vNeiBo;int m_root=-1;
};class Solution {
public:vector<int> smallestMissingValueSubtree(vector<int>& parents, vector<int>& nums) {CParentToNeiBo neiBo(parents);m_nums = nums;m_vIs1.resize(nums.size());m_ans.assign(nums.size(),1);m_vHas.resize(100'000+10);int i1 = std::find(nums.begin(), nums.end(), 1)- nums.begin();while ((-1 != i1) && (nums.size() != i1)){m_vIs1[i1] = true;i1 = parents[i1];}for (auto& v : neiBo.m_vNeiBo){for (int j = 1; j < v.size(); j++){if (m_vIs1[v[j]]){std::swap(v[0], v[j]);}}}DFS2(neiBo.m_root, neiBo.m_vNeiBo);return m_ans;}void DFS2(int cur, vector<vector<int>>& neiBo){		for (const auto& next : neiBo[cur]){DFS2(next, neiBo);}m_vHas[m_nums[cur]] = true;		if (m_vIs1[cur]){while (m_vHas[m_iNeed]){m_iNeed++;}m_ans[cur] = m_iNeed;}}vector<int> m_nums,m_ans;vector<bool> m_vIs1;int m_iNeed = 1;vector<bool> m_vHas;
};

测试用例


template<class T,class T2>
void Assert(const T& t1, const T2& t2)
{assert(t1 == t2);
}template<class T>
void Assert(const vector<T>& v1, const vector<T>& v2)
{if (v1.size() != v2.size()){assert(false);return;}for (int i = 0; i < v1.size(); i++){Assert(v1[i], v2[i]);}}int main()
{vector<int> parents,  nums;{Solution sln;parents = { -1, 0, 0, 2 }, nums = { 1, 2, 3, 4 };auto res = sln.smallestMissingValueSubtree(parents, nums);Assert({ 5,1,1,1 }, res);}{Solution sln;parents = { -1, 0, 1, 0, 3, 3 }, nums = { 5, 4, 6, 2, 1, 3 };auto res = sln.smallestMissingValueSubtree(parents, nums);Assert({ 7,1,1,4,2,1 }, res);}{Solution sln;parents = { -1, 2, 3, 0, 2, 4, 1 }, nums = { 2, 3, 4, 5, 6, 7, 8 };auto res = sln.smallestMissingValueSubtree(parents, nums);Assert({ 1,1,1,1,1,1,1 }, res);}
}

2023年2月版(当时能过)

class Solution {
public:
vector smallestMissingValueSubtree(const vector& parents, const vector& nums) {
m_c = nums.size();
m_vDirect.resize(m_c);
for (int i = 1; i < parents.size(); i++)
{
m_vDirect[parents[i]].push_back(i);
}
m_vVisiteValue.resize(m_c + 1);
m_vRet.assign(m_c, 1);
for (int i = 0; i < nums.size(); i++)
{
if (1 == nums[i])
{
DFS(i, -1,parents, nums);
break;
}
}
return m_vRet;
}
void DFS(int iCur, int iFromChild,const vector& parents, const vector& nums)
{
if (-1 == iCur)
{
return;
}
DFSForValue(iCur, iFromChild, nums);
int iMiss = (-1 == iFromChild) ? 1 : m_vRet[iFromChild];
while ((iMiss < m_vVisiteValue.size()) && (m_vVisiteValue[iMiss]))
{
iMiss++;
}
m_vRet[iCur] = iMiss;
DFS(parents[iCur], iCur, parents, nums);
}
void DFSForValue(int iCur, int iFromChild, const vector& nums)
{
m_vVisiteValue[nums[iCur]] = true;
for (auto& next : m_vDirect[iCur])
{
if (next == iFromChild)
{
continue;
}
DFSForValue(next, iFromChild, nums);
}
}
int m_c;
vector<vector> m_vDirect;
vector m_vRet;
vector m_vVisiteValue;
};

扩展阅读

视频课程

有效学习:明确的目标 及时的反馈 拉伸区(难度合适),可以先学简单的课程,请移步CSDN学院,听白银讲师(也就是鄙人)的讲解。
https://edu.csdn.net/course/detail/38771

如何你想快速形成战斗了,为老板分忧,请学习C#入职培训、C++入职培训等课程
https://edu.csdn.net/lecturer/6176

相关下载

想高屋建瓴的学习算法,请下载《喜缺全书算法册》doc版
https://download.csdn.net/download/he_zhidan/88348653

我想对大家说的话
闻缺陷则喜是一个美好的愿望,早发现问题,早修改问题,给老板节约钱。
子墨子言之:事无终始,无务多业。也就是我们常说的专业的人做专业的事。
如果程序是一条龙,那算法就是他的是睛

测试环境

操作系统:win7 开发环境: VS2019 C++17
或者 操作系统:win10 开发环境: VS2022 C++17
如无特殊说明,本算法用**C++**实现。

相关文章:

【深度优先搜索】【树】【C++算法】2003. 每棵子树内缺失的最小基因值

作者推荐 动态规划的时间复杂度优化 本文涉及知识点 深度优先搜索 LeetCode2003. 每棵子树内缺失的最小基因值 有一棵根节点为 0 的 家族树 &#xff0c;总共包含 n 个节点&#xff0c;节点编号为 0 到 n - 1 。给你一个下标从 0 开始的整数数组 parents &#xff0c;其中…...

电脑开机显示器没有信号而且键盘鼠标不亮怎么解决?

大家在使用电脑的过程,开机没有反应是比较经常遇到的问题,就有用户反映说自己的电脑启动之后,显示器无信号,键盘鼠标灯也不亮,怎么操作都没有效果。对开机有影响的硬件主要是内存条,内存条是非常容易松动的,而且金手指如果氧化了,都会导致开不了机 大家在使用电脑的过程…...

RLWE同态加密编码打包——系数打包

RLWE同态加密的明文域 RLWE的加密方案&#xff0c;如BGV、BFV&#xff0c;加密的对象&#xff0c;实际上是分圆多项式环上的一个整系数多项式。而我们在平时接触到的需要加密的数据&#xff0c;如图像或者工资&#xff0c;通常是一个数。所以&#xff0c;在使用RLWE同态加密时…...

Codeforces Round 930 (Div. 2 ABCDEF题) 视频讲解

A. Shuffle Party Problem Statement You are given an array a 1 , a 2 , … , a n a_1, a_2, \ldots, a_n a1​,a2​,…,an​. Initially, a i i a_ii ai​i for each 1 ≤ i ≤ n 1 \le i \le n 1≤i≤n. The operation swap ( k ) \texttt{swap}(k) swap(k) for an…...

【LeetCode-中等】209.长度最小的子数组-双指针/滑动窗口

力扣题目链接 1. 暴力解法 这道题的暴力解法是两层嵌套for循环&#xff0c;第一层循环从 i 0 开始遍历至数组末尾&#xff0c;第二层循环从 j i 开始遍历至找到总和大于等于 target 的连续子数组&#xff0c;并将该连续子数组的长度与之前找到的子数组长度相比较&#xff0…...

MACOS/LINUX/WINDOWS C++ 获取当前可执行程序的完整路径

依赖本人写的多平台编译器宏判断&#xff1a; C/C MACOS、Windows、Linux、HarmonyOS 平台宏判断-CSDN博客 MACOS头文件依赖&#xff1a; #if defined(_MACOS) #include <libproc.h> #endif #include <mach-o/dyld.h> 只需要链接 libSystem.dylib 就行了&#…...

【Nginx笔记02】通过Nginx服务器转发客户端的WebSocket接口到后端服务

这篇文章&#xff0c;主要介绍如何通过Nginx服务器转发客户端的WebSocket接口到后端服务【知识星球】。 目录 一、Nginx配置WebSocket 1.1、Nginx配置内容 1.2、客户端请求地址 1.3、创建WebSocket测试工程 1.4、启动测试 1.5、WebSocket超时问题 1.5.1、设置超时时间 …...

关于高德地图及其APP获取地图数据的研究

刚过完春节没几天&#xff0c;有个客户提出要获取高德地图的数据。 我看了下&#xff0c;回复说&#xff1a;这不是很简单嘛&#xff0c;高德有公开的开放平台&#xff0c;有足够的API支持用户获取数据&#xff0c;开发自己基于高德数据库的应用。 客户回复说&#xff1a;他的要…...

【Python入门教程】Python实现鸡兔同笼

今天跟大家分享一下很久之前自己做的鸡兔同笼求解问题的小游戏&#xff0c;使用公式和基本的判断语句即可实现&#xff0c;可以用来当练手或者消磨时间用。 大家在编代码的时候最重要就是先理清逻辑思路&#xff0c;例如应该套几层循环、分几个模块等等。然后在编码时可以先随意…...

微信小程序,h5端自适应登陆方式

微信小程序端只显示登陆(获取opid),h5端显示通过账户密码登陆 例如: 通过下面的变量控制: const isWeixin ref(false); // #ifdef MP-WEIXIN isWeixin.value true; // #endif...

物体检测-系列教程20:YOLOV5 源码解析10 (Model类前向传播、forward_once函数、_initialize_biases函数)

&#x1f60e;&#x1f60e;&#x1f60e;物体检测-系列教程 总目录 有任何问题欢迎在下面留言 本篇文章的代码运行界面均在Pycharm中进行 本篇文章配套的代码资源已经上传 点我下载源码 14、Model类 14.2 前向传播 def forward(self, x, augmentFalse, profileFalse):if augm…...

贪吃蛇(C语言)步骤讲解

一&#xff1a;文章大概 使用C语言在windows环境的控制台中模拟实现经典小游戏 实现基本功能&#xff1a; 1.贪吃蛇地图绘制 2.蛇吃食物的功能&#xff08;上&#xff0c;下&#xff0c;左&#xff0c;右方向控制蛇的动作&#xff09; 3.蛇撞墙死亡 4.计算得分 5.蛇身加…...

MySQL 数据库表设计和优化

一、数据结构设计 正确的数据结构设计对数据库的性能是非常重要的。 在设计数据表时&#xff0c;尽量遵循一下几点&#xff1a; 将数据分解为合适的表&#xff0c;每个表都应该有清晰定义的目的&#xff0c;避免将过多的数据存储在单个表中。使用适当的数据类型来存储数据&…...

JavaScript进阶-高阶技巧

文章目录 高阶技巧深浅拷贝浅拷贝深拷贝 异常处理throw抛异常try/caych捕获异常debugger 处理thisthis指向改变this 性能优化防抖节流 高阶技巧 深浅拷贝 只针对引用类型 浅拷贝 拷贝对象后&#xff0c;里面的属性值是简单数据类型直接拷贝值&#xff0c;如果属性值是引用数…...

C语言中“#“和“##“的用法

1. 前言 # &#xff1a;把宏参数变为一个字符串, ##&#xff1a;把两个宏参数贴合在一起. 2. 一般用法 #include<stdio.h> #define toString(str) #str //转字符串 #define conStr(a,b) (a##b)//连接 int main() { printf(toString(12345)): //输出字符串&q…...

Linux命令-clock命令(用于调整 RTC 时间)

说明 clock命令用于调整 RTC 时间。 RTC 是电脑内建的硬件时间&#xff0c;执行这项指令可以显示现在时刻&#xff0c;调整硬件时钟的时间&#xff0c;将系统时间设成与硬件时钟之时间一致&#xff0c;或是把系统时间回存到硬件时钟。 语法 clock [--adjust][--debug][--dir…...

编程笔记 Golang基础 045 math包

编程笔记 Golang基础 045 math包 一、math包主要功能常量&#xff1a;函数&#xff1a;数值运算&#xff1a;三角函数&#xff1a;对数函数&#xff1a;随机数相关&#xff1a; 二、示例代码一三、示例代码二小结 Go 语言的标准库 math 提供了一系列基础数学函数和常量&#xf…...

[Java 探索者之路] 一个大厂都在用的分布式任务调度平台

分布式任务调度平台是一种能够在分布式计算环境中调度和管理任务的系统&#xff0c;在此环境下&#xff0c;各个任务可以在独立的节点上运行。它有助于提升资源利用率&#xff0c;增强系统扩展性以及提高系统对错误的容忍度。 文章目录 1. 分布式任务调度平台1. 基本概念1.1 任…...

基于JAVA springboot+mybatis智慧生活分享平台设计和实现

基于JAVA springbootmybatis智慧生活分享平台设计和实现 博主介绍&#xff1a;多年java开发经验&#xff0c;专注Java开发、定制、远程、文档编写指导等,csdn特邀作者、专注于Java技术领域 作者主页 央顺技术团队 Java毕设项目精品实战案例《1000套》 欢迎点赞 收藏 ⭐留言 文末…...

详细了解C++中的namespace命名空间

键盘敲烂&#xff0c;月薪过万&#xff0c;同学们&#xff0c;加油呀&#xff01; 目录 键盘敲烂&#xff0c;月薪过万&#xff0c;同学们&#xff0c;加油呀&#xff01; 一、命名空间的理解 二、&#xff1a;&#xff1a;作用域运算符 三、命名空间&#xff08;namespace&…...

【大模型RAG】拍照搜题技术架构速览:三层管道、两级检索、兜底大模型

摘要 拍照搜题系统采用“三层管道&#xff08;多模态 OCR → 语义检索 → 答案渲染&#xff09;、两级检索&#xff08;倒排 BM25 向量 HNSW&#xff09;并以大语言模型兜底”的整体框架&#xff1a; 多模态 OCR 层 将题目图片经过超分、去噪、倾斜校正后&#xff0c;分别用…...

shell脚本--常见案例

1、自动备份文件或目录 2、批量重命名文件 3、查找并删除指定名称的文件&#xff1a; 4、批量删除文件 5、查找并替换文件内容 6、批量创建文件 7、创建文件夹并移动文件 8、在文件夹中查找文件...

线程与协程

1. 线程与协程 1.1. “函数调用级别”的切换、上下文切换 1. 函数调用级别的切换 “函数调用级别的切换”是指&#xff1a;像函数调用/返回一样轻量地完成任务切换。 举例说明&#xff1a; 当你在程序中写一个函数调用&#xff1a; funcA() 然后 funcA 执行完后返回&…...

条件运算符

C中的三目运算符&#xff08;也称条件运算符&#xff0c;英文&#xff1a;ternary operator&#xff09;是一种简洁的条件选择语句&#xff0c;语法如下&#xff1a; 条件表达式 ? 表达式1 : 表达式2• 如果“条件表达式”为true&#xff0c;则整个表达式的结果为“表达式1”…...

Golang dig框架与GraphQL的完美结合

将 Go 的 Dig 依赖注入框架与 GraphQL 结合使用&#xff0c;可以显著提升应用程序的可维护性、可测试性以及灵活性。 Dig 是一个强大的依赖注入容器&#xff0c;能够帮助开发者更好地管理复杂的依赖关系&#xff0c;而 GraphQL 则是一种用于 API 的查询语言&#xff0c;能够提…...

自然语言处理——Transformer

自然语言处理——Transformer 自注意力机制多头注意力机制Transformer 虽然循环神经网络可以对具有序列特性的数据非常有效&#xff0c;它能挖掘数据中的时序信息以及语义信息&#xff0c;但是它有一个很大的缺陷——很难并行化。 我们可以考虑用CNN来替代RNN&#xff0c;但是…...

实现弹窗随键盘上移居中

实现弹窗随键盘上移的核心思路 在Android中&#xff0c;可以通过监听键盘的显示和隐藏事件&#xff0c;动态调整弹窗的位置。关键点在于获取键盘高度&#xff0c;并计算剩余屏幕空间以重新定位弹窗。 // 在Activity或Fragment中设置键盘监听 val rootView findViewById<V…...

根据万维钢·精英日课6的内容,使用AI(2025)可以参考以下方法:

根据万维钢精英日课6的内容&#xff0c;使用AI&#xff08;2025&#xff09;可以参考以下方法&#xff1a; 四个洞见 模型已经比人聪明&#xff1a;以ChatGPT o3为代表的AI非常强大&#xff0c;能运用高级理论解释道理、引用最新学术论文&#xff0c;生成对顶尖科学家都有用的…...

学习STC51单片机32(芯片为STC89C52RCRC)OLED显示屏2

每日一言 今天的每一份坚持&#xff0c;都是在为未来积攒底气。 案例&#xff1a;OLED显示一个A 这边观察到一个点&#xff0c;怎么雪花了就是都是乱七八糟的占满了屏幕。。 解释 &#xff1a; 如果代码里信号切换太快&#xff08;比如 SDA 刚变&#xff0c;SCL 立刻变&#…...

AI书签管理工具开发全记录(十九):嵌入资源处理

1.前言 &#x1f4dd; 在上一篇文章中&#xff0c;我们完成了书签的导入导出功能。本篇文章我们研究如何处理嵌入资源&#xff0c;方便后续将资源打包到一个可执行文件中。 2.embed介绍 &#x1f3af; Go 1.16 引入了革命性的 embed 包&#xff0c;彻底改变了静态资源管理的…...