伴学笔记-第一篇-学习方法与心得
题目解析:选择豆包MarsCode AI 刷题(代码练习)题库中的任意题目进行解析,如思路、图解、代码详解
问题
题目描述
题目解析
题目输入按照层次遍历结果,1代表有该节点,0代表无节点。题目要求尽量少地供应暖炉,所谓暖炉就是覆盖某节点自身、父节点、子节点,求一个算法,使得覆盖二叉树的暖炉数量最少。
解题思路
贪心思想、确立遍历顺序
由题目可以知道,如果将暖炉放在根节点、叶子节点,那么就没法覆盖到其父节点、子节点,所以是浪费的,所以不要放在根节点及叶子节点。这是一种贪心的思想,确立贪心思想后,我们可以选择遍历顺序,是从根节点向下遍历(前序、层次、中序),还是从叶子节点向上遍历(后序),我们发现,根节点只有一个,节省效果有限,然而叶子节点是根节点的指数级的,节省的效果更大,所以确立从叶子节点向上遍历(后序遍历)。
这个思考方法也符合贪心思想的内涵。一个大问题可以分解成若干的子问题,找到每一个子问题的最优解,按照顺序叠加起来(前提是可以叠加子问题),那么就是这个大问题的最优解。
从另一个角度看,就是从局部最优推出整体最优,这里的局部最优就是照顾量级叶子节点的父节点安装暖炉,使得叶子节点能够节省(由于叶子节点最多,所以是数量级的,所以有说服力,可以这样分解子问题),使得整体最优:全部暖炉安装数量最小。
保障每隔两个节点放一个暖炉
要解决这个问题,除了确立遍历顺序后,还要保障怎么每隔两个节点放一个暖炉。我们需要总结一下状态的变化(状态转移)。
每一个节点可能有三种状态:
- 该节点没覆盖
- 该节点有暖炉
- 该节点被暖炉覆盖(但没有暖炉)
不妨就用3个数字来表示他们(0,1,2)
初始化
我们约定了遍历顺序和节点状态,那么需要考虑初始化过程,就是怎么使得第一个暖炉放置在叶子节点的父节点,因为叶子节点是没覆盖的,所以要使得判断条件通用,不妨假设空节点(叶子节点的子节点)是有覆盖的,那么自然而然就放到叶子节点的父节点了。
逻辑处理
接下来还得处理节点间的情况
-
左右节点都有覆盖->那么父节点无需放置暖炉
-
左右节点至少一个无覆盖->那么父节点需要放置暖炉
-
左右节点至少一个有暖炉->那么父节点无需放置暖炉
- 左右节点都有暖炉->那么父节点无需暖炉
- 左右节点有一个有暖炉,有一个有覆盖->那么父节点无需暖炉
- 左右节点有一个有暖炉,有一个无覆盖->其实我们在分解这个问题的时候,需要保障一个原则就是无需回头考虑,这里只需关注父节点是否要安装暖炉,无覆盖的那个节点应该在其下一级去判断,故,父节点无需暖炉。
-
根节点没有覆盖->结束特别判断,显然需要放置暖炉
代码与注释
python
class TreeNode:
def __init__(self, val=0, left=None, right=None):
self.val = val
self.left = left
self.right = right
def buildBinaryTree(levelOrder):
if not levelOrder:
return None
root = TreeNode(levelOrder[0])
queue = [root]
i = 1
while i < len(levelOrder):
node = queue.pop(0)
if levelOrder[i] != 0:
node.left = TreeNode(levelOrder[i])
queue.append(node.left)
i += 1
if i < len(levelOrder) and levelOrder[i] != 0:
node.right = TreeNode(levelOrder[i])
queue.append(node.right)
i += 1
return root
def solution(nodes):
# 构建二叉树的根节点
root = buildBinaryTree(nodes)
result = 0
# 深度优先遍历
def traversal(cur):
nonlocal result
if cur is None:
return 2 # 如果当前节点为空,则返回 2 表示已经暖和
left = traversal(cur.left)
right = traversal(cur.right)
if left == 2 and right == 2:
# 左右节点都已经暖和,当前节点不需要暖炉
return 0
if left == 0 or right == 0:
# 如果左右子节点中有一个需要暖炉
result += 1
return 1 # 当前节点安装暖炉
if left == 1 or right == 1:
# 左右节点中有一个已经安装了暖炉
return 2 # 当前节点已经暖和
return -1
# 如果根节点需要暖炉
if traversal(root) == 0:
result += 1
return result
if __name__ == "__main__":
# You can add more test cases here
print(solution([1, 1, 0, 1, 1]) == 1 )
print(solution([1,0,1,1,0,1,0,1,0,1,0,0,1,1]) == 3 )
print(solution([1,1,0,0,1,1,0,0,1,0,1,1,0,0,1]) == 3 )