初级算法中的树问题

初级算法中的树问题


一. 验证二叉搜索树

给定一个二叉树,判断其是否是一个有效的二叉搜索树。

假设一个二叉搜索树具有如下特征:

节点的左子树只包含小于当前节点的数。
节点的右子树只包含大于当前节点的数。
所有左子树和右子树自身必须也是二叉搜索树。

链接:https://leetcode-cn.com/leetbook/read/top-interview-questions-easy/xn08xg/
来源:力扣(LeetCode)

解法

解法1—递归

刚开始遇到这道题想着依次对左右结点与父结点的值进行比较,符合条件即可,如下:

# Definition for a binary tree node.
# class TreeNode:
#     def __init__(self, val=0, left=None, right=None):
#         self.val = val
#         self.left = left
#         self.right = right
class Solution:
    def isValidBST(self, root: TreeNode) -> bool:
        #走到末尾再返回True, 当有不符合条件时返回False,否则进行递归到末尾
    	if root is None:
        	return True
    	if root.left and root.left.val > root.val:
        	return False
    	if root.right and root.right.val < root.val:
        	return False
    	return self.isValidBST(root.left) and self.isValidBST(root.right)

但这样进行判断是错误的,只对一个中间结点与左右结点进行比较,并没有考虑二叉搜索树所有左/右子树的值都要小于/大于中间结点的值。遇到[5,4,6,null,null,3,7]这种情况就会判断出错。

所以需要记录每一层树传递下来的值的范围:

class Solution:
    def isValidBST(self, root: TreeNode) -> bool:
        return self.isValidBST_2(root, -2**32, 2**32)

    def isValidBST_2(self, node, minvalue, maxvalue):
        if node is None:
            return True
        if minvalue < node.val < maxvalue:
            return self.isValidBST_2(node.left, minvalue, node.val) and self.isValidBST_2(node.right, node.val, maxvalue)
        else:
            return False 

另外一个函数来传递每个结点可处于的范围,若不处于这个范围就返回False。
复杂度分析:

  • 时间复杂度: O ( n ) O(n) O(n),每个结点都会进行一次判断。
  • 空间复杂度: O ( n ) O(n) O(n),其中 n n n 为二叉树的节点个数。递归函数在递归过程中需要为每一层递归函数分配栈空间,所以这里需要额外的空间且该空间取决于递归的深度,即二叉树的高度。最坏情况下二叉树为一条链,树的高度为 n n n ,递归最深达到 n n n 层,故最坏情况下空间复杂度为 O ( n ) O(n) O(n)
解法2—中序遍历迭代

中序遍历顺序:左— 中 —右
当每次遍历的结点值大于上一个遍历的结点值时,符合二叉搜索树的性质。

通过一个栈来存储经过的结点,并用一个变量储存当前结点值,以供与下一个结点值进行比较。
入栈出栈的顺序(先入后出)刚好符合中序遍历结点的顺序。

class Solution:
    def isValidBST(self, root: TreeNode) -> bool:
        if root is None:
            return True
        stack = []
        minvalue = -2**32
        while root or len(stack)>0:
            # 遍历到最左侧结点
            while root:
            	# 途径结点入栈
                stack.append(root)
                root = root.left
            root = stack.pop()
            if root.val <= minvalue:
                return False
            minvalue = root.val
            root = root.right
        return True

复杂度分析:

  • 时间复杂度: O ( n ) O(n) O(n),每个结点都会进行访问。
  • 空间复杂度: O ( n ) O(n) O(n),其中 n n n 为二叉树的节点个数,栈最多会存储 n n n个结点。

2.对称二叉树

给定一个二叉树,检查它是否是镜像对称的。

在这里插入图片描述

链接:https://leetcode-cn.com/leetbook/read/top-interview-questions-easy/xn7ihv/
来源:力扣(LeetCode)

解法

解法1—递归

另外定义一个函数,负责递归比较左右两个结点的值是否相等。

若相等则继续比较左结点的左结点右结点的右结点的相等情况,左结点的右结点右结点的左结点的相等情况。

class Solution:
     def isSymmetric(self, root: TreeNode) -> bool:
        if root is None:
            return True
        else:
            return self.symmertric2(root.left, root.right)

	
    def symmertric2(self, root_left, root_right):
        if root_left is None and root_right is None:
            return True
        elif root_left is None or root_right is None:
            return False
        else:
            if root_left.val == root_right.val:
                T1 = self.symmertric2(root_left.left, root_right.right)
                T2 = self.symmertric2(root_left.right, root_right.left)
                if T1 is False or T2 is False:
                    return False
                else:
                    return True
            else:
                return False 

复杂度分析:

  • 时间复杂度: O ( n ) O(n) O(n),每个结点都会进行访问比较。
  • 空间复杂度: O ( n ) O(n) O(n),这里的空间复杂度和递归使用的栈空间有关,这里递归层数不超过 n n n,故渐进空间复杂度为 O ( n ) O(n) O(n)
解法2—迭代

利用一个队列每次存储两个对称的结点,在队列非空时进行循环。
每次循环比较两个对称点的值,并按照对称顺序将这两个点的左右子结点加入队列中。

当出现不对称情况直接返回False。

class Solution:
    def isSymmetric(self, root: TreeNode) -> bool:
        if root is None:
            return True
        node_list = []
        node_list.append(root.left)
        node_list.append(root.right)

        while len(node_list) > 0:
            # 每次两个同时出队,进行比较
            left = node_list.pop(0)
            right = node_list.pop(0)
            if left is None and right is None:
                continue
            if left is None or right is None or left.val != right.val:
                return False
            else:
                node_list.append(left.left)
                node_list.append(right.right)
                node_list.append(left.right)
                node_list.append(right.left)
        
        return True

复杂度分析:

  • 时间复杂度: O ( n ) O(n) O(n),每个结点都会进行访问比较。
  • 空间复杂度: O ( n ) O(n) O(n),将结点需要存储起来,并进行进出操作,队列中最多不超过n个点。

3. 二叉树的层序遍历

给你一个二叉树,请你返回其按 层序遍历 得到的节点值。 (即逐层地,从左到右访问所有节点)。
在这里插入图片描述
链接:https://leetcode-cn.com/leetbook/read/top-interview-questions-easy/xnldjj/
来源:力扣(LeetCode)

解法

广度优先搜索
用一个列表nodelist存储结点,根据当前一层的数量将结点值加入列表r1,并会添加下一层的结点进入nodelist,但是因为for循环范围只有当前一层所以不会对下一层结点进行操作。

当前一层结点值加入一个小列表r1,计算完毕后加入最终的列表result_list。

class Solution:
    def levelOrder(self, root: TreeNode) -> List[List[int]]:
        if root is None:
            return []
        node_list = []
        result_list = []
        node_list.append(root)
        while len(node_list) > 0:
            n = len(node_list)
            r1 = []
            # 只对前n个进行操作,因为当前一层结点数只有n个
            for i in range(n):
                curnode = node_list.pop(0)
                r1.append(curnode.val)
                if curnode.left:
                    node_list.append(curnode.left)
                if curnode.right:
                    node_list.append(curnode.right)
            result_list.append(r1)
        return result_list

复杂度分析:

  • 时间复杂度: O ( n ) O(n) O(n),每个结点都会进行访问操作。
  • 空间复杂度: O ( n ) O(n) O(n),需要依次将所有结点的值存入列表。

4.将有序数组转换为二叉搜索树

给你一个整数数组 nums ,其中元素已经按 升序 排列,请你将其转换为一棵 高度平衡 二叉搜索树。

高度平衡 二叉树是一棵满足「每个节点的左右两个子树的高度差的绝对值不超过 1 」的二叉树。
在这里插入图片描述

链接:https://leetcode-cn.com/leetbook/read/top-interview-questions-easy/xninbt/
来源:力扣(LeetCode)

解法

递归

因为要生成平衡的二叉搜索树,所以左右侧结点数要尽可能一致,因为是有序数组,所以选择数组中间的值作为根节点,左侧小于该值的数组作为左子树,右侧大于的作为右子树。

因为二叉搜索树中间结点的值位于左右结点之间,所以每次选择数组中间位置的值作为中间结点,小于的部分作为左子树,大于的部分作为右子树,进行递归操作。

将中间位置的值生成为结点,与左子树和右子树部分的中间结点相连(函数的返回值就是中间结点)。

class Solution:
    def sortedArrayToBST(self, nums: List[int]) -> TreeNode:
        n = len(nums)
        if n == 0:
            return None
        elif n == 1:
            return TreeNode(nums[0])
        else:
            t = n//2
            head = TreeNode(nums[t])
            head.left = self.sortedArrayToBST(nums[:t])
            head.right = self.sortedArrayToBST(nums[t+1:])
        return head

复杂度分析:

  • 时间复杂度: O ( n ) O(n) O(n),数组的每个值都生成一个结点并连接。
  • 空间复杂度: O ( log ⁡ n ) O(\log n) O(logn),其中 n n n 是数组的长度。空间复杂度不考虑返回值,因此空间复杂度主要取决于递归栈的深度,递归栈的深度是 O ( log ⁡ n ) O(\log n) O(logn)

为什么这么建树一定能保证是「平衡」的呢?这里可以参考「1382.将二叉搜索树变平衡」,这两道题的构造方法完全相同,这种方法是正确的,1382 题解中给出了这个方法的正确性证明:1382官方题解,感兴趣的同学可以戳进去参考。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值