leetcode hot100 108. 将有序数组转换为二叉搜索树 medium 递归


常见的搜索树类型

  1. 性质:对于树中的每一个节点,左子树的所有节点值都小于该节点的值,右子树的所有节点值都大于该节点的值。
  2. 查找效率:查找、插入和删除操作的时间复杂度在 平均情况下 是 O(log n),最坏情况下是 O(n)(当树退化成链表时)。
  3. 应用场景:广泛用于需要有序数据访问的场景,如数据库的索引。
python 复制代码
    10
   /  \
  5    15
 / \     \
3   7     20
// 查找 7:从根节点 10 开始,比较 7 和 10,向左子树搜索;接着比较 7 和 5,向右子树搜索,最终找到节点 7。
  1. 特点:为了避免最坏情况的退化,平衡二叉搜索树通过某些规则保持树的平衡,保证树的高度不会太大,从而保证查找效率。
  2. 常见类型:
    AVL 树:通过旋转操作保持树的平衡。
    红黑树:通过颜色标记和旋转来保持平衡。
  3. 应用场景:用于要求较高查找效率的场景,如操作系统中的调度算法、数据库索引等。

搜索树的操作

搜索树支持多种操作,最常见的包括:

1. 查找(Search)

  • 从根节点开始,按照树的结构进行比较,寻找特定的值
  • 在二叉搜索树中,若当前值小于节点值,则继续查找左子树;若大于节点值,则查找右子树。

2. 插入(Insert)

  • 向树中插入一个新的值。首先找到合适的位置,再插入该节点
  • 在二叉搜索树中,插入值比节点值小则插入左子树,反之插入右子树。

3. 删除(Delete)

  • 从树中删除一个值。删除操作根据节点的子树情况分为三种情况:
  • 删除的节点是叶子节点(没有子节点)。
  • 删除的节点有一个子节点。
  • 删除的节点有两个子节点。此时需要找到节点的中序后继或中序前驱,并替换节点。

4. 遍历(Traversal)

  • 对树进行遍历,常见的遍历方法有:
    前序遍历(Pre-order)
    中序遍历(In-order):用于打印有序数据。
    后序遍历(Post-order)
    层序遍历(Level-order)

有序数组和二叉搜索树(BST)之间的关系

有序数组就像是 BST 的中序遍历结果。要把一个有序数组变成一棵平衡的 BST,最简单的方法就是:永远挑数组的正中间作为根节点。

  • BST 的特性:左边比根小,右边比根大。
  • 平衡的秘诀:如果选中间的数做根,那么它左边剩下的数和右边剩下的数,数量是几乎相等的。这样长出来的左右子树高度就一致,树也就"平衡"了。

递归三部曲实现

"分治法":

  1. 出口:数组为空(或者左边界大于右边界),返回 None。
  2. 操作:
    找到数组的中点 mid。
    用 nums[mid] 创建一个新的根节点。
  3. 递归:
    把左半部分交给递归函数,去生成左子树。
    把右半部分交给递归函数,去生成右子树。

mid = (left + right) // 2:如果数组长度是偶数,这个写法会选择中间偏左的那个。


空间复杂度: O(log⁡N)O(\log N)O(logN)。主要是递归栈的深度。每次都折半,所以树的高度一定是 log⁡N\log NlogN。
时间复杂度 :O(N)O(N)O(N)。我们需要把数组里的每个数都"摸"一遍,把它们变成节点。

其实就是"二分查找"的逆过程。

二分查找是在有序数组里"找"一个数,而这道题是把二分查找的每一个"中点"连起来变成了一棵树

如果不给"有序"数组呢?

如果数组是乱序的,必须先给它排序(O(Nlog⁡N)O(N \log N)O(NlogN)),然后再用这个方法构建。


答案可能不是唯一的。如果 n 是偶数,我们可以取数组正中间左边那个数作为根节点的值,也可以取数组正中间右边那个数作为根节点的值。下面代码取的是正中间右边那个数,即下标为 n/2的数(当 n 是偶数时)。

python 复制代码
// eg: nums=[−10,−3,0,5,9]
从数组正中间的数 nums[2]=0 开始,把数组一分为二,得到两个小数组:
左:[−10,−3]。
右:[5,9]。

//答案由三部分组成:
根节点:节点值为 nums[2]=0。
把 nums[2] 左边的 [−10,−3] 转换成一棵平衡二叉搜索树,作为答案的左儿子。这是一个和原问题相似的子问题,可以递归解决。
把 nums[2] 右边的 [5,9] 转换成一棵平衡二叉搜索树,作为答案的右儿子。这是一个和原问题相似的子问题,可以递归解决

// 递归边界:如果数组长度等于 0,返回空节点。
python 复制代码
# 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 sortedArrayToBST(self, nums: List[int]) -> Optional[TreeNode]:

        # 出口:数组为空(或者左边界大于右边界),返回 None。
        if not nums:
            return None

        m = len(nums) // 2
        left = self.sortedArrayToBST(nums[:m])
        left = self.sortedArrayToBST(nums[m+1:])

        return TreeNode(nums[m], left, right)
        
        
python 复制代码
# 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 sortedArrayToBST(self, nums: List[int]) -> Optional[TreeNode]:

        def dfs(left,right):
        
            # 这里的 left == right 对应 [left, right) 区间为空
            if left == right:
                return None
            
            m = (left + right) // 2
            
            # 先递归,再组装(自底向上)
            # 注意:右开区间,所以左边是 [left, m),右边是 [m + 1, right)
            left_child = dfs(left, m)   # 递归左区间
            right_child = dfs(m + 1, right)   # 递归右区间
            
            #结束递归,返回给主函数sortedArrayToBST-> Optional[TreeNode]:
            return TreeNode(nums[m], left_child, right_child)

        return dfs(0, len(nums))
        
相关推荐
夏鹏今天学习了吗11 小时前
【LeetCode热题100(100/100)】数据流的中位数
算法·leetcode·职场和发展
元亓亓亓14 小时前
LeetCode热题100--42. 接雨水--困难
算法·leetcode·职场和发展
源代码•宸15 小时前
Leetcode—200. 岛屿数量【中等】
经验分享·后端·算法·leetcode·面试·golang·dfs
Bear on Toilet15 小时前
树_构建多叉树_41 . 实现Trie(前缀树)
开发语言·数据结构·c++·算法·leetcode
We་ct16 小时前
LeetCode 224. 基本计算器:手写实现加减+括号运算
前端·算法·leetcode·typescript
TracyCoder12318 小时前
LeetCode Hot100(35/100)——200. 岛屿数量
算法·leetcode·深度优先
We་ct20 小时前
LeetCode 141. 环形链表:两种解题思路详解
前端·算法·leetcode·链表·typescript
月挽清风21 小时前
代码随想录第29天:贪心
数据结构·算法·leetcode
望舒5131 天前
代码随想录day29,贪心算法part3
java·算法·leetcode·贪心算法
爱敲代码的TOM1 天前
程序员算法圣经-LeetCode Hot100上
算法·leetcode·哈希算法