LeetCode 题解工作台

将子数组重新排序得到同一个二叉搜索树的方案数

给你一个数组 nums 表示 1 到 n 的一个排列。我们按照元素在 nums 中的顺序依次插入一个初始为空的二叉搜索树(BST)。请你统计将 nums 重新排序后,统计满足如下条件的方案数:重排后得到的二叉搜索树与 nums 原本数字顺序得到的二叉搜索树相同。 比方说,给你 nums = [2,1…

category

10

题型

code_blocks

5

代码语言

hub

3

相关题

当前训练重点

困难 · 二分·树·traversal

bolt

答案摘要

我们设计一个函数 ,它的功能是计算以 为节点构成的二叉搜索树的方案数。那么答案就是 ,因为 计算的是以 为节点构成的二叉搜索树的方案数,而题目要求的是重排后与原数组 得到相同二叉搜索树的方案数,因此答案需要减去一。 接下来,我们来看一下 的计算方法。

Interview AiBox logo

Interview AiBox 实时 AI 助手,陪你讲清 二分·树·traversal 题型思路

试试 AiBox 面试助手arrow_forward
description

题目描述

给你一个数组 nums 表示 1 到 n 的一个排列。我们按照元素在 nums 中的顺序依次插入一个初始为空的二叉搜索树(BST)。请你统计将 nums 重新排序后,统计满足如下条件的方案数:重排后得到的二叉搜索树与 nums 原本数字顺序得到的二叉搜索树相同。

  • 比方说,给你 nums = [2,1,3],我们得到一棵 2 为根,1 为左孩子,3 为右孩子的树。数组 [2,3,1] 也能得到相同的 BST,但 [3,2,1] 会得到一棵不同的 BST 。

请你返回重排 nums 后,与原数组 nums 得到相同二叉搜索树的方案数。

由于答案可能会很大,请将结果对 109 + 7 取余数。

 

示例 1:

输入:nums = [2,1,3]
输出:1
解释:我们将 nums 重排, [2,3,1] 能得到相同的 BST 。没有其他得到相同 BST 的方案了。

示例 2:

输入:nums = [3,4,5,1,2]
输出:5
解释:下面 5 个数组会得到相同的 BST:
[3,1,2,4,5]
[3,1,4,2,5]
[3,1,4,5,2]
[3,4,1,2,5]
[3,4,1,5,2]

示例 3:

输入:nums = [1,2,3]
输出:0
解释:没有别的排列顺序能得到相同的 BST 。

 

提示:

  • 1 <= nums.length <= 1000
  • 1 <= nums[i] <= nums.length
  • nums 中所有数 互不相同 。
lightbulb

解题思路

方法一:组合计数 + 递归

我们设计一个函数 dfs(nums)dfs(nums),它的功能是计算以 numsnums 为节点构成的二叉搜索树的方案数。那么答案就是 dfs(nums)1dfs(nums)-1,因为 dfs(nums)dfs(nums) 计算的是以 numsnums 为节点构成的二叉搜索树的方案数,而题目要求的是重排后与原数组 numsnums 得到相同二叉搜索树的方案数,因此答案需要减去一。

接下来,我们来看一下 dfs(nums)dfs(nums) 的计算方法。

对于一个数组 numsnums,它的第一个元素是根节点,那么它的左子树的元素都小于它,右子树的元素都大于它。因此,我们可以将数组分为三部分,第一部分是根节点,第二部分是左子树的元素,记为 leftleft,第三部分是右子树的元素,记为 rightright。那么,左子树的元素个数为 mm,右子树的元素个数为 nn,那么 leftleftrightright 的方案数分别为 dfs(left)dfs(left)dfs(right)dfs(right)。我们可以在数组 numsnumsm+nm + n 个位置中选择 mm 个位置放置左子树的元素,剩下的 nn 个位置放置右子树的元素,这样就能保证重排后与原数组 numsnums 得到相同二叉搜索树。因此,dfs(nums)dfs(nums) 的计算方法为:

dfs(nums)=Cm+nm×dfs(left)×dfs(right)dfs(nums) = C_{m+n}^m \times dfs(left) \times dfs(right)

其中 Cm+nmC_{m+n}^m 表示从 m+nm + n 个位置中选择 mm 个位置的方案数,我们可以通过预处理得到。

注意答案的取模运算,因为 dfs(nums)dfs(nums) 的值可能会很大,所以我们需要在计算过程中对每一步的结果取模,最后再对整个结果取模。

时间复杂度 O(n2)O(n^2),空间复杂度 O(n2)O(n^2)。其中 nn 是数组 numsnums 的长度。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
class Solution:
    def numOfWays(self, nums: List[int]) -> int:
        def dfs(nums):
            if len(nums) < 2:
                return 1
            left = [x for x in nums if x < nums[0]]
            right = [x for x in nums if x > nums[0]]
            m, n = len(left), len(right)
            a, b = dfs(left), dfs(right)
            return (((c[m + n][m] * a) % mod) * b) % mod

        n = len(nums)
        mod = 10**9 + 7
        c = [[0] * n for _ in range(n)]
        c[0][0] = 1
        for i in range(1, n):
            c[i][0] = 1
            for j in range(1, i + 1):
                c[i][j] = (c[i - 1][j] + c[i - 1][j - 1]) % mod
        return (dfs(nums) - 1 + mod) % mod
speed

复杂度分析

指标
时间O(m^2)
空间O(m^2)
psychology

面试官常问的追问

外企场景
  • question_mark

    Candidate shows strong understanding of divide and conquer techniques in solving tree-based problems.

  • question_mark

    Candidate demonstrates the ability to optimize recursive solutions using dynamic programming and memoization.

  • question_mark

    Candidate is able to efficiently apply combinatorics to count valid reorderings within subarrays of the BST.

warning

常见陷阱

外企场景
  • error

    Failing to account for all possible reorderings within each subtree.

  • error

    Not properly using memoization, leading to redundant recalculations.

  • error

    Incorrectly calculating binomial coefficients or failing to apply them in subtree reorderings.

swap_horiz

进阶变体

外企场景
  • arrow_right_alt

    Handling larger tree structures with increased complexity in counting reorderings.

  • arrow_right_alt

    Introducing additional constraints, such as restricting specific numbers to particular positions.

  • arrow_right_alt

    Optimizing for space complexity, especially when dealing with very large arrays.

help

常见问题

外企场景

将子数组重新排序得到同一个二叉搜索树的方案数题解:二分·树·traversal | LeetCode #1569 困难