代码随想录算法训练营第十五天| 654.最大二叉树、617.合并二叉树、700.二叉搜索树中的搜索、98.验证二叉搜索树

写代码的第十五天,真要被递归折磨疯了。。。包括前几天的题都可以用迭代解决,但是我递归都有点懵,就先把迭代放一放。。。。可能对递归的理解现在能有30%?

654.最大二叉树

思路

凡是涉及到二叉树构造的时候都要想到前序遍历!!!!!!!先构造中再递归构造左右子树。
和昨天的最后一道题有点像,整体思路就是先找到这个数组中的最大值,这个值就是跟结点,然后这个值左侧就是左子树的结点,右侧就是右子树的结点,然后在左右子树分别重复上面的操作。
解决问题1:参数和返回值是什么?参数就是需要将数组传进来,找数组的最大值,返回值就是返回这棵树的根结点。
解决问题2:终止条件是什么?必须要保证数组中至少有一个数才能递归,如果数组是空的根本没办法进行比较最大值等操作,所以终止条件是当数组中只有一个数的时候,停止。
解决问题3:单层递归逻辑?其实就是找最大值作为跟结点的值,然后创建一个跟结点并把刚才的值赋进去,然后根据这个结点的位置进行分割数组,左侧为左子树,右侧为右子树,然后递归调用函数放在左子树和右子树中。
错误第一版:就一直显示这个index out of range,开始一只没明白这个index最大值索引怎么还能报错呢,想起了一个情况,如果说这个数组的最左侧或者最右侧是最大值呢?这个时候index在递归调用的时候就是会出问题的。所以加上if判断语句。

class Solution:
    def constructMaximumBinaryTree(self, nums: List[int]) -> Optional[TreeNode]:
        maxnum = 0
        index = 0
        if len(nums) == 1:
            root = TreeNode(nums[0])
        for i in range(len(nums)):
            if nums[i] > maxnum:
                maxnum = nums[i]
                index = i
        root = TreeNode(nums[index])
        root.left = self.constructMaximumBinaryTree(nums[:index])
        root.right = self.constructMaximumBinaryTree(nums[index+1:])
        return root

正确代码

class Solution:
    def constructMaximumBinaryTree(self, nums: List[int]) -> Optional[TreeNode]:
        maxnum = 0
        index = 0
        if len(nums) == 1:
            root = TreeNode(nums[0])
        for i in range(len(nums)):
            if nums[i] > maxnum:
                maxnum = nums[i]
                index = i
        root = TreeNode(nums[index])
        if index > 0:
            root.left = self.constructMaximumBinaryTree(nums[:index])
        if index < len(nums) - 1:
            root.right = self.constructMaximumBinaryTree(nums[index+1:])
        return root

思路(用索引)

上一版代码我们用的是每次构造一个数组,传入数组,这里面我们只传入数组的下标。设置left和right两个下标。
解决问题1:参数和返回值是什么?参数就是需要将数组传进来,left和right两个下标值(用这两个下标代表数组),返回值就是返回这棵树的根结点。
解决问题2:终止条件是什么?当这两个下标left大于等于right的时候就需要停止了。在left小于right的时候传入的数组是合理的数组,否则就不是合理的数组。
解决问题3:单层递归逻辑?其实就是找最大值作为跟结点的值,然后创建一个跟结点并把刚才的值赋进去,然后根据这个结点的位置进行分割数组,左侧为左子树,右侧为右子树,然后递归调用函数放在左子树和右子树中。在这一步中前面用的是创建一个数组存储,在这里我们只是改变left和right的值,我们现在已经找到最大值的index了,所以就是left到index是左子树,index+1到lennums是右子树,但是这是第一次使用递归的数值传入,并不是每次都是这个范围。
错误第一版:错误原因是在找最大值的for循环里面范围写了全部lennums,但是在每次递归使用找最大值的时候我们只需要判断left和right之间的数值最大值即可。

class Solution:
    def constructMaximumBinaryTree(self, nums: List[int]) -> Optional[TreeNode]:
        return self.traversal(nums,0,len(nums))

    def traversal(self,nums,left,right):
        maxnum = -1
        maxindex = -1
        if left >= right:
            return None
        for i in range(len(nums)):
            if nums[i] > maxnum:
                maxnum = nums[i]
                maxindex = i
        root = TreeNode(maxnum)
        if left < right:
            root.left = self.traversal(nums,left,maxindex)
            root.right = self.traversal(nums,maxindex+1,right)
        return root

正确代码

class Solution:
    def constructMaximumBinaryTree(self, nums: List[int]) -> Optional[TreeNode]:
        return self.traversal(nums,0,len(nums))

    def traversal(self,nums,left,right):
        maxnum = -1
        maxindex = -1
        if left >= right:
            return None
        for i in range(left,right):
            if nums[i] > maxnum:
                maxnum = nums[i]
                maxindex = i
        root = TreeNode(maxnum)
        if left < right:
            root.left = self.traversal(nums,left,maxindex)
            root.right = self.traversal(nums,maxindex+1,right)
        return root

617.合并二叉树

同时遍历两颗二叉树,第一次见。

思路(递归,改tree1)

解决问题1:参数和返回值是什么?参数就是两颗树,返回值就是返回tree1。
解决问题2:终止条件是什么?这里面是我刚开始做想不通的地方,最开始想是不是到叶子结点就终止呢,其实也不对,如果现在递归到的是第一个叶子结点呢,这时候终止后面的结点就都没有了啊,实在也是想不出别的终止条件。就去看了一下卡哥的视频,他的意思是如果tree1是空的那么要返回的就是对应位置tree2的结点(因为我们要做的是两树合并,所以当一棵树为空的时候,合并就只是把第二课树的结点放到tree1上),同理,如果tree2是空的那么就返回tree1的结点。(其实我还是不理解为什么用这个条件作为终止条件,我想的终止条件应该是两颗树所有结点都遍历完了才是终止条件)
解决问题3:单层递归逻辑?同时遍历两颗树,遍历到一个结点就先操作这个结点(就是两颗树求和赋值给tree1),然后对其左右子树在进行递归操作,这就很明显用的是前序遍历了。
注释:我之前的想法是返回值都要在终止条件里面,但是后来想了一下不是说只有终止条件才能返回,有时单次递归的时候也是需要有返回值的。就像在下面的代码里,卡哥的代码是没有if root1 = = None and root2 == None: return root1这个部分的,我理解的是这句话是代表终止条件的,而下面的两种情况只是代表其中的两种特殊情况,就是当一个结点为空另一个结点不为空的时候该怎么赋值。
正确代码

class Solution:
    def mergeTrees(self, root1: Optional[TreeNode], root2: Optional[TreeNode]) -> Optional[TreeNode]:
        if root1 == None and root2 == None:
            return root1
        if root1 == None:
            return root2
        if root2 == None:
            return root1
        root1.val = root1.val + root2.val
        root1.left = self.mergeTrees(root1.left,root2.left)
        root1.right = self.mergeTrees(root1.right,root2.right)
        return root1

新建一棵树

class Solution:
    def mergeTrees(self, root1: Optional[TreeNode], root2: Optional[TreeNode]) -> Optional[TreeNode]:
        if root1 == None and root2 == None:
            return  
        if root1 == None:
            return root2
        if root2 == None:
            return root1
        node = TreeNode()
        node.val = root1.val + root2.val
        node.left = self.mergeTrees(root1.left,root2.left)
        node.right = self.mergeTrees(root1.right,root2.right)
        return node

700.二叉搜索树中的搜索

思路(递归)

首先让跟结点的值和目标值比较,如果一致,那么就返回,如果不一致就递归着继续找,如果一直找不到那么返回none。
解决问题1:参数和返回值是什么?参数就是树以及目标值,返回值就是有一致的就返回对应的子树,没有一致的就返回None。
解决问题2:终止条件是什么?要么是找到一致的,返回子树,要么是已经遍历到空了还没有相等的,那就返回None。
解决问题3:单层递归逻辑?如果target值大于跟结点,那就在跟结点右边,继续递归,如果target值小于跟结点,那么就在跟结点左边,继续递归。
我好像有点明白递归了。。。
正确代码

class Solution:
    def searchBST(self, root: Optional[TreeNode], val: int) -> Optional[TreeNode]:
        if root == None or root.val == val:
            return root 
        if val > root.val:
            return self.searchBST(root.right,val)
        if val < root.val:
            return self.searchBST(root.left,val)
        return None

思路(迭代)

其实就是和跟结点比较大于的话就向右走,小于的话就向左走,有点像二分查找的感觉,找不到就返回None。
正确代码

class Solution:
    def searchBST(self, root: Optional[TreeNode], val: int) -> Optional[TreeNode]:
        while root != None:
            if val > root.val:
                root = root.right
            elif val < root.val:
                root = root.left
            else:
                return root
        return None

98.验证二叉搜索树

思路(踩坑哈哈哈哈哈)

每次都比较跟结点和左右子树的大小情况,递归着比较。
错误第一版:错误原因是我只比较了相邻的左中右三个结点的值的情况,并不是全局上的左都小于中都小于右!!!!

class Solution:
    def isValidBST(self, root: Optional[TreeNode]) -> bool:
        if root == None:
            return True
        if root.left != None and root.right != None and root.val > root.left.val and root.val < root.right.val:
            resultleft = self.isValidBST(root.left)
            resultright = self.isValidBST(root.right)
        if resultleft and resultright:
            return True
        return False

举个例子:
在这里插入图片描述
正确的思路:设置一个最大值初始化,左中右进行遍历,每次都比较左子树和这个maxval的大小,只要出现左子树大于他,那么直接返回false,如果左子树的值一直小于maxval那么就把此时跟结点的值赋值给macval,为了下一次递归与最大值比较。
正确代码

class Solution:
    def __init__(self):
        self.maxVal = float('-inf')  # 因为后台测试数据中有int最小值

    def isValidBST(self, root):
        if root == None:
            return True

        left = self.isValidBST(root.left)
        # 中序遍历,验证遍历的元素是不是从小到大
        if self.maxVal < root.val:
            self.maxVal = root.val
        else:
            return False
        right = self.isValidBST(root.right)

        return left and right

不设置最小值,直接取该树的最小值。初始化pre结点为空,因为我们要用用pre来不断存储上一个结点,root的上一个结点就是空,所以pre初始化为空,我们还是采用左中右的遍历方法,所以如果前一个结点的值大于后一个结点,也就是左大于中或者中大于右,那么就返回false,如果没出现这种情况,那么让pre从当前的这个root继续向下遍历。
正确代码

class Solution:
    def __init__(self):
        self.pre = None  # 用来记录前一个节点

    def isValidBST(self, root):
        if root == None:
            return True

        left = self.isValidBST(root.left)

        if self.pre != None and self.pre.val >= root.val:
            return False
        self.pre = root  # 记录前一个节点

        right = self.isValidBST(root.right)
        return left and right

思路(中序遍历)

采用中序遍历之后(中序遍历左中右),得到的结果如果是递增的,证明这棵树是二叉搜索树。
错误第一版:以下这种测试用例的情况没考虑到。二叉搜索树一定是递增的!!!!!!

class Solution:
    def isValidBST(self, root: Optional[TreeNode]) -> bool:
        res = []
        result = self.traversal(root,res)
        if result == sorted(result):
            return True
        return False
    
    def traversal(self,root,res):
        if root == None:
            return 
        self.traversal(root.left,res)
        res.append(root.val)
        self.traversal(root.right,res)
        return res

在这里插入图片描述
正确代码

class Solution:
    def isValidBST(self, root: Optional[TreeNode]) -> bool:
        res = []
        result = self.traversal(root,res)
        for i in range(len(result)):
            if i > 0 and result[i-1] >= result[i]:
                return False
        return True
    
    def traversal(self,root,res):
        if root == None:
            return 
        self.traversal(root.left,res)
        res.append(root.val)
        self.traversal(root.right,res)
        return res
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值