Leetcode 多入口DFS:树的遍历

    科技2026-04-06  9

    对于二叉树,一般认为其遍历可以委派到左右子树的遍历。同理,对于树结构,其可以委派到其所有子节点的遍历。

    vector<node*> next 用于描述其所有子节点的指针

    题目描述

    牛牛有一个算法交流群,它是这个群的群主,也是这个群实力最强的人。 算法交流群里一共有n个人,每个人都有一个等级a_iai​表示它能解决难度小于等于a_iai​的算法问题。 除了牛牛以外,群里的每个编号为i的人都在群里有一个等级比他高的朋友编号为p_ipi​。群友 i 会解决那些他产生和接收的等级小于等于a_iai​的问题,并把解决不了的问题全部交给p_ipi​。 保证牛牛的编号为1。保证牛牛的等级全场唯一且全场最高。如果牛牛解决不了他接收的问题,他将不管这些问题。  

    这天群里的每个人都产生了一个问题,牛牛知道了每个人产生问题等级k_iki​,他想知道群里的每个人在这天解决了多少问题。

     

    示例1

    输入

    复制

    4,[4,3,2,1],[1,2,3],[1,2,3,4]

    输出

    复制

    [2,2,0,0]

    说明

     

    群里一共有4个人

    4产生了等级为4的问题,4的能力值为1,无法解决,所以4号把这个问题交给了3号.4号解决问题个数为0

    3号产生了等级为3的问题,接受到等级为4的问题。3号本身等级为2,无法解决这两个问题,于是把这两个问题交给了2,自身解决问题个数为0.

    2号产生了等级为2的问题,接受到等级为3,4的两个问题。2号等级为3,解决了等级为2,3的问题,把等级为4的问题交给了1.自身解决问题个数为2

    1号产生了等级为1的问题,接受到等级为4的问题。1号自身等级为4,解决了这两个问题。自身解决问题个数为2

    备注:

     

    输入的第一个参数为整数n,代表群人数

    输入第二个参数为vector<int> a,包含n个元素,按顺序代表每个人的等级

    输入的第二个参数为vector<int> p,包含n-1个元素,按顺序代表2,3,...n号群友会找谁寻求帮助

    输入的第三个参数为vector<int> k,包含n个元素按顺序代表每个人产生的问题等级

     

     

     

    【思路一】  常规DFS :从根节点开始,不断进入DFS  叶子节点开始返回,依次解决问题,并返回不能解决的问题集合

    vector<int> dfs(int who) 返回who节点不能解决的问题集合(包含自身不能解决,以及被子节点委派的问题集合) map<int,vector<int>> mp;//mp[i]表示 i是哪些人的上级(即 i 是vector<int> 的父节点) vector<int> arr; vector<int> dfs(int who,const vector<int>&a ,const vector<int>& k ) //返回who节点不能解决问题的等级组合 { vector<int> cannot_solve;//不能解决的问题集合 //先查看自身的问题能否解决 if(a[who]>=k[who]) //能够解决自身问题 arr[who]++; else { cannot_solve.push_back(k[who]);//添加进不能解决集合 } if(mp.find(who)==mp.end()) //没有子节点 return cannot_solve; // 子节点返回的那些不能解决列表 for(int i=0;i<mp[who].size();i++) { vector<int> child_cannot_solve=dfs(mp[who][i],a,k); for(auto item:child_cannot_solve) { if(a[who]>=item) arr[who]++; else cannot_solve.push_back(item);//不能解决 添加进不能解决列表 } } return cannot_solve; } vector<int> solve(int n, vector<int>& a, vector<int>& p, vector<int>& k) { // write code here for(int i=0;i<n;i++) arr.push_back(0);//初始化 每个人能解决的问题数量为0 for(int i=0;i<p.size();i++) { int father=p[i];// 第i+2 位的父节点是 p[i] mp[father-1].push_back(i+1); //注意这里 每个人序号从1开始 } dfs(0,a,k);//从根节点开始 也就是能力最强的选手开始 return arr; }

     

     

     

    对于本问题,采取dfs做法,在存在大量节点时,将存在大量的vector<int> 拷贝,以及递归函数调用带来的开销。

    【思路二】  采取循环的方式

     

    vector<int> solve(int n, vector<int>& a, vector<int>& p, vector<int>& k) { // write code here vector<int> res(n,0);//存放结果 for(int i=0;i<k.size();i++) //分析每一件任务 { if(k[i]>a[0]) //最强的人也无法解决 continue; else if(k[i]==a[0]) //只能最强的人解决 { res[0]++; } else { int j=i; //分析第i个任务被谁解决 while(a[j]<k[i]) // 依次向父节点委派 找到能解决的人j j=p[j-1]-1;//下标从1开始 res[j]++;//由第j个人解决 } } return res; }

     

    时间复杂度分析:

    最优:每个人都独自解决  O(N)

    最差:每个人都委派到根节点 O(N*N)

     

    Processed: 0.012, SQL: 10