【每日一题】使二叉树所有路径值相等的最小代价

文章介绍了如何在满二叉树中,通过最小次数的节点值增加操作,使得从根节点到每个叶子节点的路径值相等。首先通过深度优先遍历预处理路径值,然后通过操作压缩减少操作次数,最终得到最小代价。时间复杂度为O(n),空间复杂度为O(n)或O(1)。
摘要由CSDN通过智能技术生成

6419. 使二叉树所有路径值相等的最小代价

关键词:层序遍历、深度优先

题目来源:6419. 使二叉树所有路径值相等的最小代价 - 力扣(Leetcode)

题目描述
 T层序遍历
 T深度优先

给你一个整数 n 表示一棵 满二叉树 里面节点的数目,节点编号从 1n 。根节点编号为 1 ,树中每个非叶子节点 i 都有两个孩子,分别是左孩子 2 * i 和右孩子 2 * i + 1

树中每个节点都有一个值,用下标从 0 开始、长度为 n 的整数数组 cost 表示,其中 cost[i] 是第 i + 1 个节点的值。每次操作,你可以将树中 任意 节点的值 增加 1 。你可以执行操作 任意 次。

你的目标是让根到每一个 叶子结点 的路径值相等。请你返回 最少 需要执行增加操作多少次。

  • 满二叉树 指的是一棵树,它满足树中除了叶子节点外每个节点都恰好有 2 个节点,且所有叶子节点距离根节点距离相同。
  • 路径值 指的是路径上所有节点的值之和。
朴素实现

不难想到,最终根节点到各叶子结点的路径值必然等于最初根节点到各叶子结点的最大路径值。

于是,先通过深搜,将各叶子结点到根节点的路径值预处理出来,同时找出最大路径值。之后,便可以得到各叶子结点路径值与目标路径值(即找到的最大路径值)的差值,这个差值即为对这个叶子结点需要进行的操作数。

同时,又发现,具有相同父结点的两个兄弟结点l和r,给l和r加上x,等价于给l和r的父结点+x,于是,可将对最后一层结点(叶子结点所在层)的操作进行压缩,对于倒数第二层,同样可以进行类似的操作压缩,直到压缩到根节点。

int minIncrements(int n, vector<int> &cost) {
    /**
     * 深度优先遍历:计算出每个叶子节点的路径值,并找出最大路径值
     */
    int d[n + 1], maxDep = 0;
    memset(d, 0, sizeof d);
    function<void(int, int)> dfs = [&](int u, int dep) {
        int t = dep + cost[u - 1];
        if (u > n >> 1) {
            d[u] = t, maxDep = max(t, maxDep);
            return;
        }
        dfs(u << 1, t), dfs(u << 1 | 1, t);
    };
    dfs(1, 0);

    // 计算出每个叶子结点的路径值与最大路径值的差值
    for (int i = (n >> 1) + 1; i <= n; i++)d[i] = maxDep - d[i];

    /**
     * 设左孩子、右孩子、父结点的路径值分别为l、r、p
     * 由于往父结点+v,等价于往左右孩子各+v
     * 所以,不妨把左右孩子加的公共值加到父结点上,从而减小操作次数
     * 也即(假设l≤r),p=min(l,r),l=0,r=-=l
     * 逐层将操作往上传递
     */
    int res = 0;
    while (n > 1) {
        for (int i = (n >> 1) + 1; i <= n; i += 2) {
            if (d[i] || d[i + 1]) {
                if (d[i] > d[i + 1])swap(d[i], d[i + 1]);
                d[i >> 1] = d[i];           // 公共操作施加到父结点
                res += d[i + 1] - d[i];     // 进行非公共操作
            }
        }
        n >>= 1;
    }

    return res;
}

时间复杂度:O(n)

空间复杂度:O(n)

精简实现

精简实现的核心与朴素实现相同,都是操作压缩,只是做法稍微更难想一点。

考虑一棵只有三个结点的满二叉树,设左右孩子结点为l、r,父结点为p,f(x)表示以结点x为根节点的满二叉树,使x到其各叶子结点的路径值相等后,x到叶子结点的路径值,显然,对于叶子结点,f(x)=结点值,假设f(l)<f®,要想使p到叶子结点l和r的路径值相等,只需要令f(l)等于f®,也即f(l)一直加到f®就可以了。

考虑两棵上面这样的满二叉树,父结点分别为p1和p2,经过上面的操作,p1到l1和r1的距离已经相等,即有f(p1),p2到l2和r2的距离已经相等,即有f(p2),假设f(p1)<f(p2),设p1和p2的父结点为p,要想使结点p到各叶子结点的路径值相等,只需要令f(p1)等于f(p2),也即f(p1)一直加到f(p2)即可。

依此类推,逐层往上合并,直到根节点。

int minIncrements(int n, vector<int> &cost) {
    int res = 0;
    for (int i = n - 2; i > 0; i -= 2) {
        if (cost[i] < cost[i + 1])swap(cost[i], cost[i + 1]);
        res += cost[i] - cost[i + 1], cost[i >> 1] += cost[i];
    }
    return res;
}

时间复杂度:O(n)

空间复杂度度:O(1)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值