题解 | #寻找牛妹#

寻找牛妹

http://www.nowcoder.com/practice/840e145d951341738a4ea56e14695958

题目关键信息:
n个结点之间有n-1条边,有一个目标结点数组,其中有m个目标结点,从根结点走到每个目标结点图片说明,每次可以经过最多多少条边,其中,每条边最多可以走两次
方法一:dfs
每条边最多可以走两次,因此,从根结点到目标结点的通道只能走一次,目标结点和它的所有孩子结点的通道都不用走,因此,从根结点到目标结点经过的最多边数=allPath-从根结点到目标结点的距离-目标结点的所有孩子数*2

  • 因此,可以定义一个数组存储每个结点到根结点的距离,用数组存储每个结点的孩子数,用一个二维数组list来保存每个结点以及它的相邻结点,相当于一个无向图
  • 填充数组和数组:采用dfs,从根结点开始递归搜索孩子结点,递归终止条件是遇到叶子结点,返回0,每次都将当前结点的孩子结点数量累加,最后递归得到结点的所有孩子结点数量,每个结点到根结点的距离随着递归深度增加而增加,因此用len记录深度

图片说明

Java版本

import java.util.*;


public class Solution {
    /**
     * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
     *
     * 
     * @param n int整型 
     * @param m int整型 
     * @param u int整型一维数组 
     * @param v int整型一维数组 
     * @param x int整型一维数组 
     * @return int整型一维数组
     */
    ArrayList<Integer>[]list;
    int[]dist;
    int[]child;
    public int[] solve (int n, int m, int[] u, int[] v, int[] x) {
        // write code here
        list=new ArrayList[n+1];
        dist=new int[n+1];//保存根结点到其他结点的距离
        child=new int[n+1];//每个结点的孩子数
        for(int i=1;i<=n;i++){
            list[i]=new ArrayList<>();
        }
        for(int i=0;i<n-1;i++){
            list[u[i]].add(v[i]);//无向图
            list[v[i]].add(u[i]);
        }
        dfs(1,0,0);//从第一个结点开始搜索,第一个结点的父结点初始化为0
        int[]res=new int[m];
        int allpath=2*u.length;//每条通道都走两次的数量
        for(int i=0;i<m;i++){
            res[i]=allpath-dist[x[i]]-child[x[i]]*2;//根结点到目标结点的通道只能走一次,目标结点到它所有的孩子结点的通道不用走
        }
        return res;
    }
    //curr:当前结点,pre:当前结点的父节点,len:当前结点到根结点的距离
    int dfs(int curr,int pre,int len){
        dist[curr]=len;
        int cnt=0;
        for(int c:list[curr]){
            if(c==pre)continue;//为了不重复计算通道,如果当前结点的孩子结点与它的父结点相同,直接跳过
            cnt+=dfs(c,curr,len+1)+1;//计算当前结点的孩子数,同时结点到目标结点的距离随着递归深度增加而增加
        }child[curr]=cnt;//记录每个结点的孩子数
        return cnt;
    }
}

复杂度:
时间复杂度:,只有一重循环并且dfs每个结点至少访问一次,时间复杂度为
空间复杂度:递归栈的大小不超过n,二维数组list的大小不超过,因此空间复杂度为图片说明

方法二:dfs+bfs
根结点到目标结点经过的最多边数公式为;与方法一不同的是,方法二我们采用广度优先搜索计算每个结点到根结点的距离,同时增加一个visit数组记录结点是否被访问过,因为我们在存储结点的时候是把它们当做无向图存储,所以在计算结点的孩子数和结点到根结点的距离时可能会重复访问某个结点,因此visit数组默认初始化为false,每次访问过后该结点处更改为true,下次就不会再访问该结点
用bfs计算结点到根结点的距离时,需要利用辅助队列q,先让根结点入队,访问每一个层的结点,因为每层结点到根结点的距离是相等的,因此len不变,继续下一层结点入队,直到当前层的所有结点访问完后,

图片说明
C++版本

class Solution {
public:
    /**
     * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
     *
     * 
     * @param n int整型 
     * @param m int整型 
     * @param u int整型vector 
     * @param v int整型vector 
     * @param x int整型vector 
     * @return int整型vector
     */
    vector<int> solve(int n, int m, vector<int>& u, vector<int>& v, vector<int>& x) {

        // write code here
        vector<vector<int>>list(n+1);//每个结点数组里包含孩子数组
        vector<int>dist(n+1);//记录每个结点到根结点的距离
        vector<int>child(n+1);//记录每个结点的所有孩子数
        vector<bool>visit(n+1,false);//记录结点是否被访问
        for(int i=0;i<n-1;i++){
            list[u[i]].push_back(v[i]);
            list[v[i]].push_back(u[i]);
        }
        dfs(1,list,visit,child);
        for(int i=0;i<n+1;i++)visit[i]=false;//visit需要重新初始化
        bfs(1,list,dist,visit);
        vector<int>res(m);
        int allpath=2*u.size();//每条通道都走两次的数量
        for(int i=0;i<m;i++){
            res[i]=allpath-dist[x[i]]-child[x[i]]*2;//根结点到目标结点的通道只能走一次,目标结点到它所有的孩子结点的通道不用走
        }
        return res;
    }
    void bfs(int curr,vector<vector<int>>&list,vector<int>&dist,vector<bool>&visit){
        int len=0;//len:当前结点到根结点的距离
        queue<int>q;
        q.push(curr);//先将1入队
        while(!q.empty()){
            int size=q.size();
            while(size--){//每一层结点到根结点的距离相等,因此每次一层结点一起计算
                int node=q.front();
                q.pop();
                if(visit[node])continue;//已经访问过的结点直接跳过,避免同一条通道计算两次
                visit[node]=true;
                dist[node]=len;//同一层的结点距离相等
                for(int c:list[node])//将结点的孩子入队
                    q.push(c);
            }
            len++;//下一层结点距离加一
        }
    }
    int dfs(int curr,vector<vector<int>>&list,vector<bool>&visit,vector<int>&child){
        visit[1]=true;
        int cnt=0;
        for(int c:list[curr]){
            if(!visit[c]){
                visit[c]=true;//避免重复计算同一条通道
                cnt+=dfs(c,list,visit,child)+1;//当前结点孩子数累加得到结点的所有孩子数
            }
        }child[curr]=cnt;//记录每个结点的孩子数
        return cnt;//递归终止条件是到达叶子结点,返回孩子数为0
    }
};

复杂度:
时间复杂度:dfs和bfs中,每个结点都至少访问一次,solve函数循环只有一重,因次时间复杂度为
空间复杂度:辅助队列和递归栈的大小不超过n,list二维数组的大小不超过,因次空间复杂度最差为图片说明

全部评论

相关推荐

02-12 20:22
重庆大学 Java
字节暑期刚入职四天,因为是年前,所以很多正职都放假走了,也就没有给我分配mt,然后有一个老哥在我来的时候给我发了一个landing手册,然后还有关于部门业务的白皮书,还有一些业务代码。然后本人是java面的,进来第一次接触go语言&nbsp;前面几天熟悉了一下go的语法和go的框架,可以读但是还不太会写,然后业务白皮书也看的很头疼,包括landing手册里要了解的很多东西说实话我看文档真的快看死了,一个嵌套一个,问题是我还完全不知道咋用这个我了解的东西,还有就是那个项目代码,那个老哥喊我去写写单测,熟悉一下go的语法,但也进行的很困难(这是我第一段实习,之前都是springboot那一套,真不太熟悉这个)想问问大家的建议,就是我从现在开始到在开年回来之前应该做些什么,我目前就一个想法&nbsp;就是复现一个landing手册上的go框架小项目&nbsp;就是相当于帮自己锻炼锻炼怎么写go&nbsp;或者各位大佬有没有更好的锻炼go语法的建议还有就是大家都在说vibe&nbsp;coding,那我应该怎么锻炼自己使用ai的能力,感觉我除了给一些需求然后它给我生成代码,好像就没别的用法了,那些什么工作流、拆解、skill啥的都不知道从哪一个地方开始,包括我现在正在实习,不知道精力该怎么分配,去网上想找找关于agent开发的一些学习流程,说实话,众说纷纭,有的是从python开始打基础然后系统学那些rag&nbsp;prompt&nbsp;langchain&nbsp;mcp等等,有的是说直接找一个github上的ai项目然后反复问ai,我确实有点迷茫,恳求各位大佬能留下你们宝贵的建议,我一定认真反复深刻学习有一说一&nbsp;我觉得字节饭挺好吃的!
双非后端失败第N人:1. go语言我建议你让ai带着你先把基本语法速通了,然后再去用go重新刷你以前刷过的leetcode,这样熟悉起来很快 2. 直接看你们组go项目,里面用***比较复杂,然后把每一个语法现象都喂给ai,一点点看
字节跳动公司福利 1371人发布
点赞 评论 收藏
分享
评论
点赞
收藏
分享

创作者周榜

更多
牛客网
牛客网在线编程
牛客网题解
牛客企业服务