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

题目描述:

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

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

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

注意:

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

示例 1:

输入:n = 7, cost = [1,5,2,2,3,3,1]
输出:6
解释:我们执行以下的增加操作:
- 将节点 4 的值增加一次。
- 将节点 3 的值增加三次。
- 将节点 7 的值增加两次。
从根到叶子的每一条路径值都为 9 。
总共增加次数为 1 + 3 + 2 = 6 。
这是最小的答案。

示例 2:

输入:n = 3, cost = [5,3,3]
输出:0
解释:两条路径已经有相等的路径值,所以不需要执行任何增加操作。

提示:

  • 3 <= n <= 10^5
  • n + 1 是 2 的幂
  • cost.length == n
  • 1 <= cost[i] <= 10^4

解题分析及思路:

方法:贪心

对于一个根节点 node,如果想要经过他到达叶子节点,那么node节点的左右节点的值必须相等,否则就需要增加操作。

我们可以从叶子节点开始,向上递归,每次递归都将当前节点的值增加到与其父节点的值相等,直到根节点。

以该树为例:

  • 如果需要从根节点 1 经过 2 ,到达 4 和 5 的路径和相等,那么 4 的值必须 增加 1 次 ,使得 4 的值为 3,同时将 4 和 5 的父亲节点的值增加 3,值为 8
  • 同理,如果需要从根节点 1 经过 3 ,到达 6 和 7 的路径和相等,那么 7 的值必须 增加 2 次 ,使得 7 的值为 3,同时将 6 和 7 的父亲节点的值增加 3,值为 5
  • 此时,从节点 2 达到 4 和 5 的路径和相等,不需要增加操作,从节点 3 到达 6 和 7 的路径和相等,不需要增加操作
  • 但是从根节点 1 到达 2 和 3 的路径和不想等,同样的操作需要将 3 的值 增加 3 次 ,使得 3 的值为 8
  • 整个过程,增加了六次操作

那为什么不从上累积到下呢?

因为从下到上的话,每次都需要将父节点的值增加到与其子节点的值相等,这样会导致重复增加操作。

那为什么不层级遍历到最大值,并将每层的节点与最大值的差值累加呢?

因为层级遍历,会增加不需要增加的次数。

例如[764, 1460, 2664, 764, 2725, 4556, 5305, 8829, 5064, 5929, 7660, 6321, 4830, 7055, 3761]

原值为764的节点,仅需增加abs(max(8829, 5064) + 764 - max(5929, 7660) + 2725) = 792次,如果采用层级遍历,那么他需要增加 5305 - 764 = 4541次,这样会导致增加不需要增加的次数。

func minIncrements(n int, cost []int) int {
	ans := 0
	for i := n - 2; i > 0; i -= 2 {
		ans += abs(cost[i] - cost[i+1])
		// 叶节点 i 和 i+1 的双亲节点下标为 i/2(整数除法)
		cost[i/2] = cost[i/2] + max(cost[i], cost[i+1])
	}
	return ans
}

func abs(x int) int {
	if x < 0 {
		return -x
	}
	return x
}

func max(i, j int) int {
	if i > j {
		return i
	}
	return j
}

复杂度:

  • 时间复杂度:O(N),其中 N 值为 n
  • 空间复杂度:O(1)

执行结果:

  • 执行耗时:136 ms,击败了79.17% 的Go用户
  • 内存消耗:8.3 MB,击败了12.50% 的Go用户

通过次数 22K 提交次数 31.1K 通过率 70.7%

Related Posts

1026. 节点与其祖先之间的最大差值

## 题目描述:给定二叉树的根节点 root,找出存在于 不同 节点 A 和 B 之间的最大值 V,其中 V = |A.val - B.val|,且 A 是 B 的祖先。(如果 A 的任何子节点之一为 B,或者 A 的任何子节点是 B 的祖先,那么我们认为 A 是 B 的祖先)示例 1: ![](/img/leetcode/1026节点与其祖先之间的最大差值/tmp-tre

read more

1038. 从二叉搜索树到更大和树

## 题目描述:给定一个二叉搜索树 root (BST),请将它的每个节点的值替换成树中大于或者等于该节点值的所有节点值之和。提醒一下, 二叉搜索树 满足下列约束条件:- 节点的左子树仅包含键 小于 节点键的节点。 - 节点的右子树仅包含键 大于 节点键的节点。 - 左右子树也必须是二叉搜索树。*示例 1:***![](/img/leetcode/

read more

105. 从前序与中序遍历序列构造二叉树

## 题目描述:给定两个整数数组 preorder 和 inorder ,其中 preorder 是二叉树的先序遍历, inorder 是同一棵树的中序遍历,请构造二叉树并返回其根节点。示例 1: ``` 输入: preorder = [3,9,20,15,7], inorder = [

read more

106. 从中序与后序遍历序列构造二叉树

## 题目描述:给定两个整数数组 inorder 和 postorder ,其中 inorder 是二叉树的中序遍历, postorder 是同一棵树的后序遍历,请你构造并返回这颗 二叉树 。示例 1: ``` 输入:inorder = [9,3,15,20,7], postorder

read more

109. 有序链表转换二叉搜索树

## 题目描述:给定一个单链表的头节点 head ,其中的元素 按升序排序 ,将其转换为高度平衡的二叉搜索树。本题中,一个高度平衡二叉树是指一个二叉树每个节点 的左右两个子树的高度差不超过 1。示例 1: ``` 输入: head = [-10,-3,0,5,9] 输出: [0,-3,9

read more

114. 二叉树展开为链表

## 题目描述:给你二叉树的根结点 root ,请你将它展开为一个单链表:- 展开后的单链表应该同样使用 TreeNode ,其中 right 子指针指向链表中下一个结点,而左子指针始终为 null 。 - 展开后的单链表应该与二叉树 先序遍历 顺序相同。示例 1: ``` 输入:root

read more