834. 树中距离之和

这篇博客讨论了如何解决834题——树中距离之和的问题。通过后序遍历处理内部子树计算节点内部子树到节点的距离之和,然后利用先序遍历处理外部子树,更新节点之间的距离之和。博主提供了详细的思路和完整的代码实现。
摘要由CSDN通过智能技术生成

834. 树中距离之和

给定一个无向、连通的树。树中有 N 个标记为 0…N-1 的节点以及 N-1 条边 。

第 i 条边连接节点 edges[i][0] 和 edges[i][1] 。

返回一个表示节点 i 与其他所有节点距离之和的列表 ans。

思路

在这里插入图片描述
如图,节点2到其他所有节点距离之和可以分解为两部分:

  • 节点2内部子树节点到节点2的距离之和。
  • 节点2外部节点到节点2的距离之和。

定义dist[i]表示第i个节点内部子树节点到节点i的距离之和,那么dist[2]怎么求呢?
在这里插入图片描述

  • dist[2] += dist[3] + 2,因为节点3子树有两个节点,需要加上2次红色路径。
  • dist[2] += dist[4] + 1,节点4只有一个节点。

因此,还需要定义nodeNum[i]表示节点i所在子树的节点数量,初始化均为1。思路基本形成了,在递归树的同时计算节点数量,同时计算dist,那么递归的base case是什么呢?很容易想到,叶子节点就是base case,此时dist[i] = 0,nodeNum[i] = 1,其只有一条路径连向父节点。只要从叶子节点开始处理,更新dist和nodeNum即可。先处理叶子节点,再处理根节点的递归树方式是什么呢?很明显就是后序遍历,因此代码如下:

后序遍历处理内部子树

    void postOrder(int u, int v) {
    	//graph是类似邻接表,存储节点的邻居信息
        for(auto& neighbor : graph[u]) {
            //当前节点的邻居为父节点,跳过
            //邻居只有父节点,base case结束递归
            if(neighbor == v) {
                continue;
            }
            //后序遍历
            postOrder(neighbor, u);
            //计算dist
            dist[u] += dist[neighbor] + nodeNum[neighbor];
            //更新nodeNum
            nodeNum[u] += nodeNum[neighbor]; 
        }
    }

处理好节点2内部子树节点到节点2的距离之和之后,也就是计算好dist之后,还需要计算节点2外部节点到节点2的距离之和。
在这里插入图片描述
如图,节点0的dist[0]是正确的,因为其是根节点,可不可以从这个正确的dist[0]推出其他节点正确的dist[i]呢?如图,dist[2]是存储了节点2内部子树节点到节点2的距离,dist[0]是存储了节点0内部子树节点到节点2的距离。

  • 节点2外部子树的全部节点到节点0的距离 + 节点2外部子树全部节点数 = 节点2外部子树的全部节点到节点2的距离
  • 节点2内部子树的全部节点带节点0的距离 - 节点2内部字数全部节点数 = 节点2内部子树的全部节点到节点2的距离
  • 两者相加,其实用公式来表示就是dist[0] + N - nodeNum[2] - nodeNum[2] = dist[2]

因而可以从根节点不断更新子节点的dist值,从根节点更新子节点,使用的是先序遍历。

先序遍历处理外部子树

    void preOrder(int u, int v) {
        for(auto& neighbor : graph[u]) {
            if(neighbor == v) {
                continue;
            }
            dist[neighbor] = dist[u] - nodeNum[neighbor] + nodeNum.size() - nodeNum[neighbor];
            preOrder(neighbor, u);
        }
    }

综上,只需要后序遍历+先序遍历就可以解决问题。

全部代码

class Solution {
public:
    vector<int> dist, nodeNum;
    vector<vector<int>> graph;
    void postOrder(int u, int v) {
        for(auto& neighbor : graph[u]) {
            if(neighbor == v) {
                continue;
            }
            postOrder(neighbor, u);
            dist[u] += dist[neighbor] + nodeNum[neighbor];
            nodeNum[u] += nodeNum[neighbor]; 
        }
    }
    void preOrder(int u, int v) {
        for(auto& neighbor : graph[u]) {
            if(neighbor == v) {
                continue;
            }
            dist[neighbor] = dist[u] - nodeNum[neighbor] + nodeNum.size() - nodeNum[neighbor];
            preOrder(neighbor, u);
        }
    }

    vector<int> sumOfDistancesInTree(int N, vector<vector<int>>& edges) {
        //距离初始化为0
        dist.resize(N, 0);
        //节点数初始化为1
        nodeNum.resize(N, 1);
        graph.resize(N);
        //构建图邻接表
        for(auto& edge : edges) {
            int u = edge[0], v = edge[1];
            graph[u].push_back(v);
            graph[v].push_back(u);
        }
        //后序遍历
        postOrder(0, -1);
        //前序遍历
        preOrder(0, -1);
        return dist;
    }
};
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值