SumOfDistancesInTree [source code]


public class SumOfDistancesInTree {
static
/****************************************************************************/
class Solution {
    Map<Integer, Set<Integer>> adjlists;
    int[] count;
    int[] sum;
    int N;

    public int[] sumOfDistancesInTree(int N, int[][] edges) {
        adjlists = new HashMap<> ();
        this.N = N;
        for (int[] edge : edges) {
            adjlists.computeIfAbsent (edge[0], num -> new HashSet<> ()).add (edge[1]);
            adjlists.computeIfAbsent (edge[1], num -> new HashSet<> ()).add (edge[0]);
        }
        count = new int[N];
        sum = new int[N];
        bottom_up (0, -1);
        top_down (0, -1);
        return sum;
    }

    void bottom_up (int node, int parent) {
        count[node] = 1;
        for (int nei : adjlists.getOrDefault (node, Collections.emptySet ())) {
            if (nei != parent) {
                bottom_up (nei, node);
                count[node] += count[nei];
                sum[node] += count[nei] + sum[nei];
            }
        }
    }

    void top_down (int node, int parent) {
        for (int nei : adjlists.getOrDefault (node, Collections.emptySet ())) {
            if (nei != parent) {
                sum[nei] = sum[node] - count[nei] + (N - count[nei]);
                top_down (nei, node);
            }
        }
    }
}
/****************************************************************************/

    public static void main(String[] args) {
        SumOfDistancesInTree.Solution tester = new SumOfDistancesInTree.Solution();
        int[][][] inputs = {
            {{6}}, {{0,1},{0,2},{2,3},{2,4},{2,5}}, {{8,12,6,10,10,10}},
            {{1}}, {}, {{0}},
        };
        for (int i = 0; i < inputs.length / 3; i++) {
            System.out.println (Printer.separator ());
            int N = inputs[3 * i][0][0];
            int[][] edges = inputs[3 * i + 1];
            int[] ans = inputs[3 * i + 2][0], output = tester.sumOfDistancesInTree (N, edges);
            String ans_str = Printer.array (ans), output_str = Printer.array (output);
            System.out.printf ("N: %d\nedges: \n%s%s\n%s\n", 
                N, Matrix.printMatrix (edges), Printer.wrap (output_str, output_str.equals (ans_str) ? 92 : 91), ans_str
            );
        }
    }
}

很精干的一个题目, 应该是有些窍门在里面;

简单做法就是直接把这个tree给建立起来, 然后算距离; 因为每一个node的距离之和都要返回, 所以估计有一些可以避免重复计算的技巧在里面?

一个简单的想法就是一个returned dfs返回结果, 上层root来组合; 但是有一个问题, 比如给的这个例子;

  0  
 / \  
1   2  
   /|\  
  3 4 5

你可能会觉得root不好选, 不过我认为, undirected tree里面root的定义其实是相对的; 谁做root都可以;

因为是DFS, 所以是一个Bottom-Up的过程, 那么2会返回(2,3)=1, (2,4)=1, (2,5)=1这种, 然后给0, 0就开始, 因为他知道自己到3,4,5的距离, 但是问题来了, 2怎么知道自己到0的距离, 甚至到1的距离呢?

这个倒是有点像sum-product algorithm: 考虑给的例子你计算1的结果和计算5的结果, 不是吗?

不过说起来, sumproduct其实我也不是写的很熟, 因为当时作业做的其实只是一个chain的形状; 这里是一个general的tree, 怎么写? 用什么顺序来更新message?

但是思路我觉得好像可能是跟这个有关系的, 应该是根据这题要稍微定制一下?

算了, 用简单写法先乱写写;

最后还是失败了, 用的是一个N^2的算法, 每一个node向外propogate自己的distance出去; 但是被一个大case给TLE了, 所以这题应该是希望得到一个优于N^2的算法; 这个时候的代码, 其实还是调了半天的;

class Solution {      
    public int[] sumOfDistancesInTree(int N, int[][] edges) {  
        Map<Integer, List<Integer>> adjlists = new HashMap<> ();  
        for (int[] edge : edges) {  
            if (edge.length == 0)  
                continue;  
            adjlists.computeIfAbsent (edge[0], num -> new ArrayList<> ()).add (edge[1]);  
            adjlists.computeIfAbsent (edge[1], num -> new ArrayList<> ()).add (edge[0]);  
        }  
        int[][] dist = new int[N][N];  
        Queue<Integer> queue = new LinkedList<> ();  
        for (int i = 0; i < N; i++) {  
            queue.offer (i);  
            int depth = 1;  
            boolean[] visited = new boolean[N];  
            visited[i] = true;  
            while (!queue.isEmpty ()) {  
                int size = queue.size ();  
                for (int _i = 0; _i < size; _i++) {  
                    int cur = queue.poll ();  
                    for (int nei : adjlists.getOrDefault (cur, Collections.emptyList ())) {  
                        if (visited[nei])  
                            continue;  
                        visited[nei] = true;  
                        queue.offer (nei);  
                        if (nei > i) {  
                            dist[i][nei] += depth;  
                            dist[nei][i] += depth;                             
                        }  
                    }  
                }  
                depth++;  
            }  
        }  
        int[] ans = new int[N];  
        for (int i = 0; i < N; i++) {  
            for (int j = 0; j < N; j++) {  
                ans[i] += dist[i][j];  
            }  
        }  
        return ans;  
    }  
}

这个代码:

        for (int[] edge : edges) {  
            adjlists.computeIfAbsent (edge[0], num -> new ArrayList<> ()).add (edge[1]);  
            adjlists.computeIfAbsent (edge[1], num -> new ArrayList<> ()).add (edge[0]);  
        }

居然被一个{}edges给break了, 我都想不通; 凭什么啊, edges如果是空的, 那么这个循环不是应该不执行吗?

java> edges = new int[][] {{}}  
int[][] edges = [[]]  
java> for (int [] edge : edges) {System.out.printf ("%d, %d\n", edge[0], edge[1]);}  
java.lang.ArrayIndexOutOfBoundsException: 0

还真的是, 这个是什么原理? 哦对的, 上面这个是报错是正常的, 因为唯一的一个edge的长度不对;

java> edges = new int[][] {}  
int[][] edges = []  
java> for (int [] edge : edges) {System.out.printf ("%d, %d\n", edge[0], edge[1]);}

你看这个是没毛病的: edges是空的, 所以不执行; 我上面好像就是我自己代码写错了;

然后这个小技巧:

for (int nei : adjlists.getOrDefault (cur, Collections.emptyList ()))

也是看过的, 如果不这样写一个default, get出来的可能是Null;

大概扫了一眼editorial, 给的是O(N)的算法, 果然我还是naive了;

2018-05-17 18:26:49
这次给的几道题居然全都是Google的, 毕竟题多;

另外, 回头想想, 其实我自己的这个所谓的propagate的做法, 跟直接每一个点发一个BFS没有任何的区别;

最后就是按照editorial的这个解法写了一个然后AC了; 感觉这个题目虽然很好, 但是最后解法比较限制化? 好像大部分的实现细节都没有办法改动; 比如两个DFS你有没有办法改成returned recursion? 当然是可以改的, 比如就是故意重复的把count和sum这两个数组刚刚更新完的内容给传上去; 但是这个其实本身还是依赖于全局数组;
这个是有点类似我们当时写sum-product的情况的, 就是要维护一个全局数组(NLP当时写的时候, 维护的是Map, 不过是一样的), 所以这里这个mutation recursion好像是无法避免的;


UNFINISHED


editorial

Approach #1: Subtree Sum and Count [Accepted]

Intuition and Algorithm

感觉awice也是变懒了, 现在intuition和algorithm全都扔在一起写;
他本来algorithm部分习惯性就写的不好, 这样他写的这些讲解就更不容易看懂了;

Root the tree. For each node, consider the subtree of that node plus all descendants, and consider count[node] and stsum[node], the number of nodes and the sum of the value of those nodes.

这里讲的很有误区, 实际上stsum里面存的并不是value, value这样一写, 让你感觉好像是要把node.val这个给加进去一样; 他这里写value, 实际上就是一个general的概念, 比如answer value这样之类的概念;

We can calculate this using a post-order traversal, where on exiting some node, the count and stsum of all descendants of this node is correct, and we now calculate count[node] += count[nei] and stsum[node] += stsum[nei] + count[nei].

这个所谓的correct是什么意思呢? 其实什么也没说; 如上面提到的, stsum的定义, 其实他一点都没有给清楚;

This will give us the right answer for the root: ans[root] = stsum[root].

Now for the insight: if we have a node parent and it's child child, then ans[child] = ans[parent] - count[child] + (N - count[child]). This is because there are count[child] nodes that are 1 easier to get to from child than parent, and N-count[child] nodes that are 1 harder to get to from child than parent.

Using a second, pre-order traversal, we can update our answer in linear time for all of our nodes.

class Solution {  
    int[] ans, count;  
    List<Set<Integer>> graph;  
    int N;  
    public int[] sumOfDistancesInTree(int N, int[][] edges) {  
        this.N = N;  
        graph = new ArrayList<Set<Integer>>();  
        ans = new int[N];  
        count = new int[N];  
        Arrays.fill(count, 1);  

        for (int i = 0; i < N; ++i)  
            graph.add(new HashSet<Integer>());  
        for (int[] edge: edges) {  
            graph.get(edge[0]).add(edge[1]);  
            graph.get(edge[1]).add(edge[0]);  
        }  
        dfs(0, -1);  
        dfs2(0, -1);  
        return ans;  
    }  

    public void dfs(int node, int parent) {  
        for (int nei: graph.get(node))  
            if (nei != parent) {  
                dfs(nei, node);  
                count[node] += count[nei];  
                ans[node] += ans[nei] + count[nei];  
            }  
    }  

    public void dfs2(int node, int parent) {  
        for (int nei: graph.get(node))  
            if (nei != parent) {  
                ans[nei] = ans[node] - count[nei] + N - count[nei];  
                dfs2(nei, node);  
            }  
    }  
}

awice现在写代码也不讲究了, global随便用了; 不过这个其实我自己以前也讲过了, 并不是不好的习惯; 真正面试的时候该用global的时候就用global, 会让白板代码轻松很多;

代码本身很简单, 关键是理解背后的计算;

判断nei!=parent这个是因为这里是一个undirected, 所以不让DFS回头而已;

不是很懂, 不浪费时间, 先打一个trace, 看看他到底在干什么;

这个是默认例子结束的时候, count和ans的情况(分别是左边黄色的和右边绿色的数字):

好像跟我自己直接手算的结果有点区别?
注意看哦, 虽然说是给的时候看起来像是一个graph问题, 但是最后实际上真正解决的时候还是用tree的思路来解决的;
这个做法能够拓展吗? 感觉不行; 就是这题想要利用recursion性质所以发现这一点?
不过这一题一开始条件里面给的这个tree的信息也是很明显的: V-1个edge, 不是tree是什么呢; 这样一个没有明显讲是tree, 但是一看就知道是tree, 反而有点欲盖弥彰, 就是告诉你你最好这题要用到这个条件;

哦, 我自己手算的时候忘记看到那个一开始count全都初始化到1的;

那么第一个DFS的计算是没问题了; 下一个问题, 他计算的是什么? 也就是这里这个绿字, 代表的是什么呢?

虽然count和ans是同时更新的, 但是理解的时候并没有必要将两者联系在一起来理解; 当然了, ans的更新最后实际上确实依赖了count更新的结果;

也就是说, 你知道count计算的就是一个subtree的count, 然后ans比如计算的是什么就行了; 虽然ans需要借用count的结果, 但是这里两个一起Bottom-Up更新, 跟假如一开始先一个单独的DFS把count全都更新好, 然后再单独一个DFS专门去更新ans, 其实是没有区别的, 就是因为这里的顺序安排;

看起来很简单, 但是感觉真正理解起来其实很复杂啊?

感觉这里这个ans的作用, 光是看一层的root关系, 比如只看一个root和自己的child之间的关系, 都看不出来的; 就用给的这个例子, 感觉要看两层, 才能比较好的看出来是大概是什么意思;

紫红色的这些线, 代表对这个8的贡献里面的5, 这些实际上是好计算的, 他们就跟count的计算是差不多的; 不信你看紫红色的5个, 再加上node 0自己这个1, 最后得到的正好就是6;
关键是ans实际上还多计算了三个, 就是紫色线带的这三个;
通过这个两层root level的分析, 起码知道是再算什么了;

这篇文章最后给的得分很低, 3分都不到, 这个也是大家都对awice这个风格有点不满意了; 越来越懒散, 这里最后stsum也就是ans的定义, 以及为什么要这样算, 从头到尾一句都没有提过; 光说一个correct, right answer, 问题是这个是什么意思呢?

后面大概猜测一下, 他这个所谓的right answer, 放在这里node 0这里来看, 大概意思就是0这个node现在的ans的值就是已经对应的: sum of distances to all others的意思;

那么先不管其他的node, 我们想一下, 为什么这个DFS走完了之后, 就可以确保0的结果已经获得了呢? 他用的是什么技巧呢?

他这个把所有的count先初始化到1的做法, 也不值得提倡; 我手写trace的时候好几次因为这个算错; 根本没有必要用这个初始化; 既然是count计算, 这个+1的过程明确的放在recursion算式里面, 让代码的意图更加明确;

为了更好的理解这个问题, 我重新手算了一个更深的tree:

again, 为了理解这个DFS到底完成的是什么, 我们还是用Bottom-Up的方式来理解这个trace;

左边的数字就是count, 这个我们是很熟悉的; 下面, 我们尝试理解, 右边的这些数字是怎么来的? 不一定要立刻就开始看最上面的这个49, 哪怕是从34, 甚至是10开始看, 都是可以的; 我之所以选择重新画一个更深的tree, 就是想要保证我确实理解他这里的这个recursion关系;

还是Bottom-Up, 从最底下的, 类似induction的initialization开始思考; 0可以不用管, 所以2其实是自己node的两个child贡献的;
然后看10, 10是怎么来的? 两个child subtree里面的每一个node, 都贡献了1, 所以这个就是10的3+3的部分; 除开这个, 还有2+2, 是什么呢? 是继续向下多走一层的4个, 在leaf位置的node, 各自贡献1;
同样的, 看34, 首先7+7很好理解, 所有的descendant都贡献1, 这个直接借用count的计算, 也是很正常的; 10+10是什么?
感觉可以大概找到这个ans的定义了. 在这个Bottom-Up的过程当中, 假定如果我们定义ans是这样: sum of distances of all descendants. 注意, 这个跟题目要求的定义的区别; 题目是sum of distances to any other nodes, 我们这里限制于, 只计算跟自己的descendant的; 那么第一个DFS完成的这个计算, 是合理的完成了sum of distances of all descendants这个计算的; 不信你可以看, 10确实就是24 + 1 2这个结果; 只不过与其用乘法, awice这里给的这个是一个加法的解决方案; 这种思路以前也是见到过的, 好像是长度N求组合个数的时候用到过: 避免使用需要乘法的数学公式, 直接用基本的线性加法来完成计算;

好理解的计算方式是10=24 + 12, 而awice的计算方式是10=3+2+3+2; 有什么关系?
当然了, 当理解了ans的定义是sum of distances of all descendants之后, 他的两个, 尤其是对ans的这个recursion公式就很好理解了;
不对, 也不是那么好理解; 还是得先理解这个加法计算是怎么跟naive的乘法计算结合起来的;
乘法计算这个叫法不好, 如果是一个unbalanced的tree, 那么这个乘法也不成立了; 实际上naive做法还是加法计算, 只不过加法的思路不同;

正常做法, 这里root的10, 应该是2+2+2+2 + 1+1+1+1这样的;
然后我们看他的算法;
3+3实际上算的是什么? 实际上是下面这6个node, 每一个人实际上最后只给了root1; 这个显然是不够的, 因为看我们上面这个计算, 最下面这四个node, 应该给的是2, 而不是1;
那么为什么用下层的ans可以弥补这个问题呢?
感觉sum of distances of all descendants这个定义好像还不够完整?

我好像懂了, 这个定义是没有问题的; 还是这个小tree; 我们抽象一下;

可以看到, 我们首先还是假设ans[B]和ans[C]满足上面sum of distances of all descendants这个定义;
你想象这两个subtree有很多的点;
那么现在你想要算ans[A], 你怎么算?
能不能直接把ans[B]和ans[C]一加就拉倒?
肯定是不能的, 因为这两个sum是到B和C的距离; 你到A的距离, 还少1; 注意, 这个少1是谁少? 所有的B subtree里面的node都少了1! (C同理), 包括B自己;
这个也就是这里这个count的作用;

awice这里这个算法的抽象程度相当的高;
所以如果面试当中要讲这个算法, 思路应该是这样

  • 首先, 不用general的graph的思路去做, 因为给了一个是tree的隐藏条件, 所以用tree来思考很合情合理; 那么在tree的场合下面, 我先尝试找sum of distances of all descendants这个定义, 因为他有方向性, 所以有recursion性质;
  • 然后先说, 我希望找一个recursive的关系, 然后用我上面这个图, 要找一个什么样的recursion关系; 然后看看ans[B] + ans[C]能不能得到ans[A], 然后发现问题, 数字缺少了; 缺少的这个怎么计算? 然后subtree里面的每一个node都要补1, 那么需要count就出来了; 注意看到这里这个count虽然本身计算很容易, 但是能想到是很难的; 你上来就说, 我要算count, 那基本就凉了;

好的, milestone一下; 为什么呢, 因为你发现没有, 你到现在只不过才看完他的解法的一半而已; DFS2还没有开始看;

DFS2的这个过程实际上就是我说的, 有点类似sum-product的第二个pass的过程, 因为你看, 他是用parent来计算child了, 也就是说跟第一个pass换了一个方向;
这个是完全符合sum-product算法当时的思路的;

DFS2的代码结构跟DFS1的代码结构很类似, 不过你脑子里面过一下(还是可以用Bottom-Up的思路来过, 不过因为是PreOrder, 所以作用不如之前那么明显), 可以发现这个做法的思路有点类似eager dp填表的思路; 因为你到了一个node之后, 你站在这个node这里, 你马上就要把自己的nei全部都更新掉; 而不是说, 我等到到了nei再去更新;
这样一个PreOrder, 就导致这个更新最后是一个对于tree来说Top-Down的做法: 这个是一个很重要的性质, 因为我们都知道, tree如果是普通的recursion, 大部分时候最后的顺序, 本质上都是一个Bottom-Up的过程, 这里用一个类似pre pre order的做法(因为是PreOrder, 而且是在pre位置就开始更新), 完成了实质意义上的Top-Down;

如果只是普通的PreOrder, 能不能做到? 也就是到了一个node, 立刻更新自己的ans? 好像也可以?
不过再pre位置更新的一个好处, 就类似我们以前说LinkedList类题目的时候提到过的, 你再parent位置, 也就是pre位置, 把该做的事情做完了, 是更好的选择, 省的去维护parent, 甚至去回头了;

回头重新看awice给的解读:

Now for the insight: if we have a node parent and it's child child, then ans[child] = ans[parent] - count[child] + (N - count[child]). This is because there are count[child] nodes that are 1 easier to get to from child than parent, and N-count[child] nodes that are 1 harder to get to from child than parent.

这个我现在回头看, 其实感觉他讲的还是挺好的; 继续重新看上面给的那个抽象tree, 应该能大概理解这个意思; 反正就是你就是知道你parent的结果了, 你child的ans应该是什么样的; 然后分成两半的对比;

B自己的subtree里面, 有countB个node, 都是距离B自己更近, 所以如果我们基于ansA来进行计算, 那么这么多countB个node, 每一个要减少1;
同样的, C那里的, 全都要+1; 因为实际上可能有很多个C, 所以针对C来考虑不是很方便, 而是直接用把B自己排除的思路来计算;
N-countB对不对呢? 实际上, 把B自己的subtree排除掉, 就剩下两种情况, 要么是A自己, 要么是B的sibling: 比如这里唯一的C;

其实这里有一个小窍门, 首先, 针对C这个subtree, 那么所有的countC个, 全都要+1, 这个是很好计算的;
关键是, N - countB, 包含的不仅是countC, 实际上还包含了A! 当然, 这个最后是统一处理了; 因为对于B来说, A是有一个从0到1的转变的; 所以最后N - countB这个计算, 是完全没有问题的: 确实是有N - countB个node需要+1;

这个结果可以在假如A只有B和C这两个child的情况下验证; 首先, 如果我就是让你直接算B的newsum, 你怎么算? 也就是说, 假如你可以用C的结果, 那么是很简单的; 然后你验证一下他的计算:

可以看到, 是完全没有问题的; again, 你要意识到为什么再你计算B的新结果的时候, 你不应该用C的结果; 同样你也应该已经理解, 为什么只要用A的结果, 就可以很轻松的给B计算结果;

所以回头想想, 我一开始的怀疑是正确的, 这题最后用的就是sum-product算法的变形; 无非就是我没有这个本事写出来而已 :)

另外看到我当时担心的一个问题, 就是message传递的这个顺序, 好像并无所谓, 只要保证是tree, 随便选一个root, 然后leaf开始传(对应于DFS1 PostOrder的Bottom-Up的实际过程), 最后会和到root之后, root重新往下传(PreOrder)就行了;

总体来说这个真的是一个相当高质量的hard题, 不仅是通过一个经典算法改编过来的, 而且还考察了你知不知道用DFS来实现一个双向的message传递: 实际上, 这一点我还真的是没有想到; 这个是我刚开始一拿到题目的时候并没有意识到的一个难点; 毕竟chain模型里面, 直接两个线性计算就结束了; 一个general的tree的时候, 线性iteration肯定就是不行了; 没想到最后这个iteration居然是借助于DFS的behavior来完成的;

这个问题暂时就这样了, 先睡觉, 明天起来依靠回忆实现一下; 好像没有太多实现上的小细节所以估计不太难; 这个问题的难点还是在于想法, 两个DFS部分其实都很难想到;

如果实际面试的时候, sum-product算法是不是也可以提一下? 尤其是作为你怎么想到第一个DFS的ans的定义这些东西的motivation;


discuss还没有仔细看, 不过最高票那个lee给的答案, 实际上跟这里editorial给的是一样的;

lee这个答案是用seen的一个HashSet来维护顺序访问, awice这里直接用一个类似parent link的技巧来避免回头访问, 反正都是见到过的技巧; 事实上, awice的这个技巧是可以拓展或者说记住的;
你可能会说, 不就是走一个tree吗, 以前怎么没这么麻烦? 这个是因为这里的是一个undirected的tree, 以前大多数时候走的都是一个本身就不可以回头的directed tree, 所以没有这个需要避免回头的设定;
again, 这里也看到了, 避免回头的方法很简单, 记录parent link就行了;


uwi: 15min

class Solution {  
    public int[] sumOfDistancesInTree(int n, int[][] edges) {  
        int[] from = new int[n-1];  
        int[] to = new int[n-1];  
        for(int i = 0;i < n-1;i++){  
            from[i] = edges[i][0];  
            to[i] = edges[i][1];  
        }  
        int[][] g = packU(n, from, to);  
        int[][] pars = parents3(g, 0);  
        int[] par = pars[0], ord = pars[1], dep = pars[2];  

        int[] dp = new int[n];  
        int[] des = new int[n];  
        Arrays.fill(des, 1);  
        for(int i = n-1;i >= 1;i--){  
            des[par[ord[i]]] += des[ord[i]];  
        }  
        for(int i = n-1;i >= 0;i--){  
            int cur = ord[i];  
            for(int e : g[cur]){  
                if(par[cur] == e)continue;  
                dp[cur] += dp[e] + des[e];  
            }  
        }  
        for(int i = 1;i < n;i++){  
            int cur = ord[i];  
            int p = par[cur];  
            dp[cur] += dp[p] - dp[cur] - des[cur] + (n - des[cur]);  
        }  

        return dp;  
    }  

    public int[][] parents3(int[][] g, int root) {  
        int n = g.length;  
        int[] par = new int[n];  
        Arrays.fill(par, -1);  

        int[] depth = new int[n];  
        depth[0] = 0;  

        int[] q = new int[n];  
        q[0] = root;  
        for (int p = 0, r = 1; p < r; p++) {  
            int cur = q[p];  
            for (int nex : g[cur]) {  
                if (par[cur] != nex) {  
                    q[r++] = nex;  
                    par[nex] = cur;  
                    depth[nex] = depth[cur] + 1;  
                }  
            }  
        }  
        return new int[][] { par, q, depth };  
    }  

    int[][] packU(int n, int[] from, int[] to) {  
        int[][] g = new int[n][];  
        int[] p = new int[n];  
        for (int f : from)  
            p[f]++;  
        for (int t : to)  
            p[t]++;  
        for (int i = 0; i < n; i++)  
            g[i] = new int[p[i]];  
        for (int i = 0; i < from.length; i++) {  
            g[from[i]][--p[from[i]]] = to[i];  
            g[to[i]][--p[to[i]]] = from[i];  
        }  
        return g;  
    }  
}

感觉能看懂的希望很渺茫了;


Problem Description

An undirected, connected tree with N nodes labelled 0...N-1 and N-1 edges are given.

The ith edge connects nodes edges[i][0] and edges[i][1] together.

Return a list ans, where ans[i] is the sum of the distances between node i and all other nodes.

Example 1:

Input: N = 6, edges = [[0,1],[0,2],[2,3],[2,4],[2,5]]  
Output: [8,12,6,10,10,10]  
Explanation:   
Here is a diagram of the given tree:  
  0  
 / \  
1   2  
   /|\  
  3 4 5  
We can see that dist(0,1) + dist(0,2) + dist(0,3) + dist(0,4) + dist(0,5)  
equals 1 + 1 + 2 + 2 + 2 = 8.  Hence, answer[0] = 8, and so on.

Note: 1 <= N <= 10000

Difficulty:Hard
Total Accepted:124
Total Submissions:889
Contributor:lee215

Companies
google
Related Topics
treedepth-first search

results matching ""

    No results matching ""