作者推荐
动态规划的时间复杂度优化
本文涉及知识点
深度优先搜索
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++**实现。




















