算法训练Day20 | LeetCode654. 最大二叉树(构造二叉树应用);617. 合并二叉树(操作两个二叉树);700. 二叉搜索树中的搜索(BST特性);98.验证二叉搜索树(BST的双指针

目录

LeetCode654. 最大二叉树

1. 思路

2. 代码实现

3. 复杂度分析

4. 思考与收获

LeetCode617. 合并二叉树

方法一:递归法

1. 思路

2. 代码实现

3. 复杂度分析

4. 思考与收获

方法二:迭代解法

1. 思路

2. 代码实现

3. 复杂度分析

4. 思考与收获

700. 二叉搜索树中的搜索

方法一:递归解法

1. 思路

2. 代码实现

3. 复杂度分析

4. 思考与收获

方法二:迭代解法

1. 思路

2. 代码实现

3. 复杂度分析

4. 思考与收获

LeetCode98.验证二叉搜索树

1. 思路

2. 代码实现

实现一:判断 递归中序遍历的数组 是否有序;

实现二:直接在中序递归的过程中判断是否有序

实现方法三:双指针

实现方法四:迭代法

3. 复杂度分析

4. 思考与收获


LeetCode654. 最大二叉树

链接: 654. 最大二叉树 - 力扣(LeetCode)

1. 思路

本题和中序和后序构造一颗二叉树几乎完全一样,就是分割点的找寻有点不同;

凡是二叉树的题,都涉及到一个问题:遍历顺序是什么?

构造二叉树类型的题目,都一定要先构造中间节点,才能构造左右节点,因此凡是构造二叉树类型的题目,都要用前序遍历,也就是“中左右”。

💡 构造树一般采用的是前序遍历,因为先构造中间节点,然后递归构造左子树和右子树。

按照如下步骤:

  1. 第一步,确定递归函数的参数和返回值;

    参数就是传入的是存放元素的数组,返回该数组构造的二叉树的头结点,返回类型是指向节点的指针;

  2. 第二步,确定终止条件;

    题目中说了输入的数组大小一定是大于等于1的,所以我们不用考虑小于1的情况,那么当递归遍历的时候,如果传入的数组大小为1,说明遍历到了叶子节点了;那么应该定义一个新的节点,并把这个数组的数值赋给新的节点,然后返回这个节点。 这表示一个数组大小是1的时候,构造了一个新的节点,并返回;

    细节:但是如果采用这样的终止条件的话,在递归遍历的时候,就要控制进入递归的数组大小一定≥1!!

    所以如果终止条件写 if(len(nums) == 0): return None;就可以在递归的时候不用对nums的长度做判断了。

  3. 单层递归的逻辑,“ 中左右” 处理;

    • 首先要找到数组中最大的值和对应的下标,最大的值构造根节点,下标用来下一步分割数组;

    • 最大值所在的下标左区间 构造左子树;

      这里要判断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. 思考与收获

  1. 本题和LeetCode105、LeetCode106都非常类似,都是构造二叉树类的题目,构造二叉树都用前序遍历;

  2. 本题也可以类似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
    
  3. 在递归处理的时候加不加if限制条件实际上是要和终止处理一一对应上的,如果终止条件允许空节点进入递归,就不用判断if;如果不允许,就需要判断;一般情况来说:如果让空节点(空指针)进入递归,就不加if,如果不让空节点进入递归,就加if限制一下, 终止条件也会相应的调整;

Reference:

  1. 力扣
  2. 代码随想录 (programmercarl.com)

本题学习时间:60分钟。


LeetCode617. 合并二叉树

链接: 617. 合并二叉树 - 力扣(LeetCode)

方法一:递归法

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就是合并之后树的根节点(就是修改了原来树的结构)。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值