LeetCode之将有序数组转换为二叉搜索树(108)、二叉搜索树的最近公共祖先(235)、二叉搜索树中的众数(501)、修剪二叉搜索树(669)

11 篇文章 0 订阅

1、有序数组转换为二叉搜索树(108)

题目描述:

【简单题】
将一个按照升序排列的有序数组,转换为一棵高度平衡二叉搜索树。

本题中,一个高度平衡二叉树是指一个二叉树每个节点 的左右两个子树的高度差的绝对值不超过 1。

示例:

在这里插入图片描述

题目链接

思路分析

  • 元素是有序的,要生成一棵BST,且要求BST是平衡的。也就是说对于二叉树的每个结点,要让其左子树与右子树的结点数量尽可能相等。
  • 二叉搜索树的中序遍历即为有序序列,中序遍历的顺序为:左子树 → \to 根节点 → \to 右子树。
  • 因此,我们可以选择数组的中间点作为根结点。
  • 如果我们选定数组的索引为i的元素为根结点,那么左子树与右子树结点的值的区间就唯一确定了(因为数组有序)。左子树结点值的区间[0, i - 1],右子树结点值的区间[i + 1, nums.length - 1]。要让BST平衡,那么所选的i就应该选数组的中间元素。
  • 平衡BST的左子树,右子树也均是平衡BST,所以也需要用同样的策略去选值。于是可以将区间[0, num.length - 1]上的问题转化为两个子问题[0, mid]与[mid + 1, num.length - 1]上构建平衡二叉树。这就是递归的结构。

【代码实现】

# Definition for a binary tree node.
# class TreeNode(object):
#     def __init__(self, x):
#         self.val = x
#         self.left = None
#         self.right = None

class Solution(object):
    def sortedArrayToBST(self, nums):
        """
        :type nums: List[int]
        :rtype: TreeNode
        """
        def helper(left,right):
            if left>right:
                return None
        
            mid_index=(left+right)//2
            root=TreeNode(nums[mid_index])#根结点
            root.left=helper(left,mid_index-1)
            root.right=helper(mid_index+1,right)
            return root 
        return helper(0,len(nums)-1)
  • 时间复杂度: O ( n ) O(n) O(n),其中n 是数组的长度。每个数字只访问一次。

  • 空间复杂度: O ( log ⁡ n ) O(\log n) O(logn),其中n 是数组的长度。空间复杂度不考虑返回值,因此空间复杂度主要取决于递归栈的深度,递归栈的深度是 O ( log ⁡ n ) O(\log n) O(logn)

2、二叉搜索树的最近公共祖先(235)

题目描述:

给定一个二叉搜索树, 找到该树中两个指定节点的最近公共祖先。

【名词解释】

最近公共祖先

“对于有根树 T 的两个结点 p、q,最近公共祖先表示为一个结点 x,满足 x 是 p、q 的祖先且 x 的深度尽可能大(一个节点也可以是它自己的祖先)。”

例如,给定如下二叉搜索树: root = [6,2,8,0,4,7,9,null,null,3,5]

在这里插入图片描述
题目链接

题解一——递归

思路分析

1、从根结点遍历树;
2、如果指定节点p,q都在左子树上,那么最近公共祖先在左子树上,则以左孩子为根结点调用函数;
3、如果指定节点p,q都在右子树上,那么最近公共祖先在右子树上,则以右孩子为根结点调用函数;
4、如果指定节点p,q一个在右子树上,一个在左子树上,则最近公共祖先为根结点。

如何判定节点在根结点的左子树还是在右子树上?

二叉搜索树的性质就派上用场了:

  • 节点左子树上所有节点的值都小于节点的值
  • 节点右子树上所有节点的值都大于节点的值
  • 左子树与右子树都是二叉搜索树

【代码实现】

# Definition for a binary tree node.
# class TreeNode(object):
#     def __init__(self, x):
#         self.val = x
#         self.left = None
#         self.right = None

class Solution(object):
    def lowestCommonAncestor(self, root, p, q):
        """
        :type root: TreeNode
        :type p: TreeNode
        :type q: TreeNode
        :rtype: TreeNode
        """
        if p.val<root.val and q.val<root.val:
            return self.lowestCommonAncestor(root.left,p,q)
        elif p.val>root.val and q.val>root.val:
            return self.lowestCommonAncestor(root.right,p,q)
        else:
            return root
  • 时间复杂度: O ( N ) O(N) O(N)
    其中 N 为 BST 中节点的个数,在最坏的情况下我们可能需要访问 BST 中所有的节点。

  • 空间复杂度: O ( N ) O(N) O(N)
    所需开辟的额外空间主要是递归栈产生的,之所以是 N N N 是因为 BST 的高度为 N N N

题解二——迭代

关键是找分割节点,分割点就是能让节点 p 和节点 q 不能在同一颗子树上的那个节点,或者是节点 p 和节点 q 中的一个,这种情况下其中一个节点是另一个节点的父亲节点。

当根结点不为空时,执行以下操作:

  • 1、如果指定节点p,q都在左子树上,那么根结点更新为其左孩子
  • 2、如果指定节点p,q都在右子树上,那么根结点更新为其右孩子
  • 3、否则,返回根节点
# Definition for a binary tree node.
# class TreeNode(object):
#     def __init__(self, x):
#         self.val = x
#         self.left = None
#         self.right = None

class Solution(object):
    def lowestCommonAncestor(self, root, p, q):
        """
        :type root: TreeNode
        :type p: TreeNode
        :type q: TreeNode
        :rtype: TreeNode
        """
        while root:
            if p.val<root.val and q.val<root.val:
                root=root.left
            elif p.val>root.val and q.val>root.val:
                root=root.right
            else:
                return root
  • 时间复杂度: O ( N ) O(N) O(N)
    其中 N 为 BST 中节点的个数,在最坏的情况下我们可能需要访问 BST 中所有的节点。

  • 空间复杂度: O ( 1 ) O(1) O(1)

3、二叉搜索树中的众数(501)

题目描述:

【简单题】
给定一个有相同值的二叉搜索树(BST),找出 BST 中的所有众数(出现频率最高的元素)。

假定 BST 有如下定义:

结点左子树中所含结点的值小于等于当前结点的值
结点右子树中所含结点的值大于等于当前结点的值
左子树和右子树都是二叉搜索树

在这里插入图片描述
题目链接

思路分析

1、众数:出现最多次数的数
2、二叉搜索树性质:中序遍历为递增序列

3、由二叉搜索树中序遍历性质得两邻节点值相等的可能性:当前结点与前一节点相等。在统计元素出现次数中,如同双指针的做法,这里也需要记录上一个结点TreeNode pre;这样才能知道当前结点值与谁比较;另外还需要记录某个值的出现次数counter,以及出现次数的最大值max_counter(否则你咋知道谁出现最多次)。并且这里遍历过程中的众数信息需要记录(List存放众数)及更新。

4、建立中序遍历辅助函数:一边遍历,一边比较数值,得到众数表

  • 特殊情况:如果根结点为空,返回空
  • 中序遍历左子树

统计次数,比较,更新众数表

  • 如果当前结点值与上一个结点值相等,那么这个数字的出现次数+1。否则,清零。
  • 更新上一节点pre为当前结点值
  • 如果上一个数字出现次数counter最大,需要更新众数信息。首先更新最大出现次数max_count = counter;然后将之前记录的众数清空,再将上一个数字放入众数表中
  • 如果一个数字出现次数等于最大出现次数,那么目前来看,它也是可能的众数,加入列;
  • 否则,上一个数字一定不是众数,不管它,继续保留List中的数字。
  • 中序遍历右子树

上述中需要初始化当前次数,最大次数,众数表
【代码实现】

# class TreeNode:
#     def __init__(self, x):
#         self.val = x
#         self.left = None
#         self.right = None

class Solution:
    def __init__(self):
        self.max_counter = 0 #初始化最大次数为0
        self.pre = float("-inf") #设置前一节点值为负无穷
        self.counter = 0 #初始化当前结点次数为0
    def findMode(self, root):
        res = [] #存储众数
        def inorder(root): # 中序遍历,遍历过程中比较数值
            if not root:
                return None
            inorder(root.left) 
            if root.val == self.pre:    #如果相等counter加1,不等counter清0
                self.counter += 1
            else:
                self.counter = 0
            self.pre = root.val #更新前一结点为当前结点
            if self.counter > self.max_counter: #与最多的比较,大于则清空列表,重新添加,相等则继续添加,小于则保持
                self.max_counter = self.counter
                res.clear()
                res.append(root.val)
            elif self.counter == self.max_counter:
                res.append(root.val)

            inorder(root.right)
            return res
        return inorder(root)
  • 时间复杂度: O ( N ) O(N) O(N)
    其中 N 为 BST 中节点的个数,在最坏的情况下我们可能需要访问 BST 中所有的节点。

  • 空间复杂度: O ( 1 ) O(1) O(1)

4、修剪二叉搜索树(669)

题目描述:

【简单题】
给定一个二叉搜索树,同时给定最小边界L 和最大边界 R。通过修剪二叉搜索树,使得所有节点的值在[L, R]中 (R>=L) 。你可能需要改变树的根节点,所以结果应当返回修剪好的二叉搜索树的新的根节点。

在这里插入图片描述
在这里插入图片描述
题目链接

思路分析

\quad \quad 题目要求所有节点的值在在[L, R]中 (R>=L),考虑二叉搜索树的性质:

  • 节点左子树上所有节点的值都小于节点的值
  • 节点右子树上所有节点的值都大于节点的值
  • 左子树与右子树都是二叉搜索树

\quad \quad 结合性质以及题目要求,可产生以下思路求解此题:

1、修剪一棵树,如果根结点的值小于给定的左边界L,那么当前结点及其左子树就会被修剪掉,修剪后的树应该是其右子树,但是右子树不一定是符合范围的树,所以要对其右子树叶进行修剪,然后返回修剪后的右子树。

2、同理,根结点的值大于给定的右边界R,修剪后的树应该是其左子树且要对左子树修剪。

3、涉及到改变树的结构,就需要更新链接,如果当前结点值在范围内,那么修建其左右子树,并且更新左右链接。最后将当前修剪好的子树返回。

【python代码实现】

# Definition for a binary tree node.
# class TreeNode(object):
#     def __init__(self, x):
#         self.val = x
#         self.left = None
#         self.right = None

class Solution(object):
    def trimBST(self, root, L, R):
        """
        :type root: TreeNode
        :type L: int
        :type R: int
        :rtype: TreeNode
        """
        if not root:
            return 
        elif root.val<L:
            return self.trimBST(root.right,L,R)
        elif root.val>R:
            return self.trimBST(root.left,L,R)
        else:
            root.left=self.trimBST(root.left,L,R)
            root.right=self.trimBST(root.right,L,R)
            return root
  • 时间复杂度: O ( N ) O(N) O(N)
    其中 N 为 BST 中节点的个数,在最坏的情况下我们可能需要访问 BST 中所有的节点。

  • 空间复杂度: O ( N ) O(N) O(N),即使我们没有明确使用任何额外的内存,在最糟糕的情况下,我们递归调用的栈可能与节点数一样大。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值