目录
LeetCode654. 最大二叉树
1. 思路
本题和中序和后序构造一颗二叉树几乎完全一样,就是分割点的找寻有点不同;
凡是二叉树的题,都涉及到一个问题:遍历顺序是什么?
构造二叉树类型的题目,都一定要先构造中间节点,才能构造左右节点,因此凡是构造二叉树类型的题目,都要用前序遍历,也就是“中左右”。
💡 构造树一般采用的是前序遍历,因为先构造中间节点,然后递归构造左子树和右子树。
按照如下步骤:
-
第一步,确定递归函数的参数和返回值;
参数就是传入的是存放元素的数组,返回该数组构造的二叉树的头结点,返回类型是指向节点的指针;
-
第二步,确定终止条件;
题目中说了输入的数组大小一定是大于等于1的,所以我们不用考虑小于1的情况,那么当递归遍历的时候,如果传入的数组大小为1,说明遍历到了叶子节点了;那么应该定义一个新的节点,并把这个数组的数值赋给新的节点,然后返回这个节点。 这表示一个数组大小是1的时候,构造了一个新的节点,并返回;
细节:但是如果采用这样的终止条件的话,在递归遍历的时候,就要控制进入递归的数组大小一定≥1!!
所以如果终止条件写 if(len(nums) == 0): return None;就可以在递归的时候不用对nums的长度做判断了。
-
单层递归的逻辑,“ 中左右” 处理;
-
首先要找到数组中最大的值和对应的下标,最大的值构造根节点,下标用来下一步分割数组;
-
最大值所在的下标左区间 构造左子树;
这里要判断maxValueIndex > 0,因为要保证左区间至少有一个数值;
-
最大值所在的下标右区间 构造右子树
判断maxValueIndex < (nums.size() - 1),确保右区间至少有一个数值;
-
2. 代码实现
# 类似中序和后序构造二叉树
# 递归解法 time:O(N^2);space:O(N)
class Solution(object):
def constructMaximumBinaryTree(self, nums):
"""
:type nums: List[int]
:rtype: TreeNode
"""
# 第一步 判断结束条件
if len(nums) == 0: return
# 第二步 找到数组中最大的值和对应的下标
maxValueIndex = -1
maxValue = -float("inf") # 这里也可以直接写成0,因为值都为正整数
for i in range(len(nums)):
if nums[i] > maxValue:
maxValueIndex = i
maxValue = nums[i]
# 第三步 递归处理
root = TreeNode(maxValue) # 中
root.left = self.constructMaximumBinaryTree(nums[:maxValueIndex]) # 左
root.right = self.constructMaximumBinaryTree(nums[maxValueIndex+1:]) #右
return root
3. 复杂度分析
时间复杂度:O(n^2)
其中 n 是数组 nums 的长度;在最坏的情况下,数组严格递增或递减,需要递归 n 层,第 i (0≤i<n) 层需要遍历 n-i 个元素以找出最大值,总时间复杂度为 O(n^2);
空间复杂度:O(n)
即为最坏情况下需要使用的栈空间。
4. 思考与收获
-
本题和LeetCode105、LeetCode106都非常类似,都是构造二叉树类的题目,构造二叉树都用前序遍历;
-
本题也可以类似LeetCode105优化,不去构造新数组,操作左右下标的区间就可以了;注意类似用数组构造二叉树的题目,每次分隔尽量不要定义新的数组,而是通过下标索引直接在原数组上操作,这样可以节约时间和空间上的开销;(以下代码直接copy的,二刷再写)
class Solution: """最大二叉树 递归法""" def constructMaximumBinaryTree(self, nums: List[int]) -> TreeNode: return self.traversal(nums, 0, len(nums)) def traversal(self, nums: List[int], begin: int, end: int) -> TreeNode: # 列表长度为0时返回空节点 if begin == end: return None # 找到最大的值和其对应的下标 max_index = begin for i in range(begin, end): if nums[i] > nums[max_index]: max_index = i # 构建当前节点 root = TreeNode(nums[max_index]) # 递归构建左右子树 root.left = self.traversal(nums, begin, max_index) root.right = self.traversal(nums, max_index + 1, end) return root
-
在递归处理的时候加不加if限制条件实际上是要和终止处理一一对应上的,如果终止条件允许空节点进入递归,就不用判断if;如果不允许,就需要判断;一般情况来说:如果让空节点(空指针)进入递归,就不加if,如果不让空节点进入递归,就加if限制一下, 终止条件也会相应的调整;
Reference:
本题学习时间:60分钟。
LeetCode617. 合并二叉树
方法一:递归法
1. 思路
相信这道题目很多同学疑惑的点是如何同时遍历两个二叉树呢?其实和遍历一个树逻辑是一样的,只不过传入两个树的节点,同时操作。
二叉树使用递归,就要想使用前中后哪种遍历方式?
如果直接在root1 上修改数值的话,本题使用哪种遍历都是可以的!但是如果是重新创建一个新的二叉树,不改变tree1和tree2的结构的话,应该要用中序遍历,“ 中左右”, 因为要先把中间节点创建出来,才可以定义它的左右节点;
我们下面以前序遍历为例。
2. 代码实现
那么我们来按照递归三部曲来解决:
- 确定递归函数的参数和返回值:
首先那么要合入两个二叉树,那么参数至少是要传入两个二叉树的根节点,返回值就是合并之后二叉树的根节点。
代码如下:
def mergeTrees(self, root1: TreeNode, root2: TreeNode) -> TreeNode:
- 确定终止条件:
因为是传入了两个树,那么就有两个树遍历的节点t1 和 t2,如果t1 == NULL 了,两个树合并就应该是 t2 了啊(如果t2也为NULL也无所谓,合并之后就是NULL)。
反过来如果t2 == NULL,那么两个数合并就是t1(如果t1也为NULL也无所谓,合并之后就是NULL)。
细节:这样写比同时给tree1和tree2加条件更简洁,其中包含了tree1和tree2同时为空的情况;
# 递归终止条件:
# 但凡有一个节点为空, 就立刻返回另外一个.
# 如果另外一个也为None就直接返回None.
if not root1: return root2
if not root2: return root1
- 确定单层递归的逻辑:
单层递归的逻辑就比较好些了,这里我们用重复利用一下t1这个树,t1就是合并之后树的根节点(就是修改了原来树的结构)。