二叉搜索树题目:将有序数组转换为二叉搜索树

题目

标题和出处

标题:将有序数组转换为二叉搜索树

出处:108. 将有序数组转换为二叉搜索树

难度

4 级

题目描述

要求

给定整数数组 nums \texttt{nums} nums,其中元素已经按升序排列,将其转换为高度平衡二叉搜索树。

高度平衡二叉树满足每个结点的左右子树的高度差的绝对值不超过 1 \texttt{1} 1

示例

示例 1:

示例 1.1

输入: nums   =   [-10,-3,0,5,9] \texttt{nums = [-10,-3,0,5,9]} nums = [-10,-3,0,5,9]
输出: [0,-3,9,-10,null,5] \texttt{[0,-3,9,-10,null,5]} [0,-3,9,-10,null,5]
解释: [0,-10,5,null,-3,null,9] \texttt{[0,-10,5,null,-3,null,9]} [0,-10,5,null,-3,null,9] 也将被视为正确答案:

示例 1.2

示例 2:

示例 2

输入: nums   =   [1,3] \texttt{nums = [1,3]} nums = [1,3]
输出: [3,1] \texttt{[3,1]} [3,1]
解释: [1,null,3] \texttt{[1,null,3]} [1,null,3] [3,1] \texttt{[3,1]} [3,1] 都是高度平衡二叉搜索树。

数据范围

  • 1 ≤ nums.length ≤ 10 4 \texttt{1} \le \texttt{nums.length} \le \texttt{10}^\texttt{4} 1nums.length104
  • -10 4 ≤ nums[i] ≤ 10 4 \texttt{-10}^\texttt{4} \le \texttt{nums[i]} \le \texttt{10}^\texttt{4} -104nums[i]104
  • nums \texttt{nums} nums严格递增顺序排列

解法

思路和算法

由于二叉搜索树的中序遍历序列是单调递增的,因此给定的升序数组即为二叉搜索树的中序遍历序列。在只有中序遍历序列的情况下,无法唯一地确定二叉搜索树。

为了得到高度平衡二叉搜索树,构造的二叉搜索树应满足根结点的左子树和右子树的结点数尽可能接近。当结点总数是奇数时,根结点值应为中序遍历序列的中间位置的结点值,根结点的左子树和右子树的结点数应相等;当结点总数是偶数时,根结点值应为中序遍历序列的中间位置的两个结点值之一,根结点的左子树和右子树的结点数之差的绝对值应等于 1 1 1

确定高度平衡二叉搜索树的根结点之后,其余的结点值分别位于根结点的左子树和右子树中,数组中位于根结点左侧的值都在左子树中,数组中位于根结点右侧的值都在右子树中,左子树和右子树也是高度平衡二叉搜索树。可以通过数学归纳法证明,如果两个高度平衡二叉搜索树的结点数之差的绝对值不超过 1 1 1,则这两个高度平衡二叉搜索树的高度之差的绝对值不超过 1 1 1

由于高度平衡二叉搜索树的每个子树也都是高度平衡二叉搜索树,每个子树包含的结点值的集合对应给定的数组中的连续子数组,因此可以使用递归的方式构造高度平衡二叉搜索树,递归的过程中只要指定每个子树包含的结点值的集合对应的连续子数组的下标区间 [ start , end ] [\textit{start}, \textit{end}] [start,end] 即可。

递归的终止条件是下标区间为空,即 start > end \textit{start} > \textit{end} start>end,此时对应的子树为空。对于其余情况,首先根据 start \textit{start} start end \textit{end} end 计算得到根结点值的下标 mid \textit{mid} mid 并使用该结点值创建根结点,然后分别使用下标区间 [ start , mid − 1 ] [\textit{start}, \textit{mid} - 1] [start,mid1] [ mid + 1 , end ] [\textit{mid} + 1, \textit{end}] [mid+1,end] 创建根结点的左子树和右子树。

start ≤ end \textit{start} \le \textit{end} startend 时, mid \textit{mid} mid 的取值的唯一性取决于下标区间 [ start , end ] [\textit{start}, \textit{end}] [start,end] 内的元素个数的奇偶性。如果下标区间 [ start , end ] [\textit{start}, \textit{end}] [start,end] 内的元素个数是奇数,则 mid \textit{mid} mid 的取值是唯一的;如果下标区间 [ start , end ] [\textit{start}, \textit{end}] [start,end] 内的元素个数是偶数,则 mid \textit{mid} mid 的取值是不唯一的,可以是中间位置左边的下标或者中间位置右边的下标。

  • mid = ⌊ start + end 2 ⌋ \textit{mid} = \Big\lfloor \dfrac{\textit{start} + \textit{end}}{2} \Big\rfloor mid=2start+end 时, mid \textit{mid} mid 是中间位置左边的下标。

  • mid = ⌊ start + end + 1 2 ⌋ \textit{mid} = \Big\lfloor \dfrac{\textit{start} + \textit{end} + 1}{2} \Big\rfloor mid=2start+end+1 时, mid \textit{mid} mid 是中间位置右边的下标。

如果下标区间 [ start , end ] [\textit{start}, \textit{end}] [start,end] 内的元素个数是奇数,则上述两种方法计算得到的 mid \textit{mid} mid 的值相同。

由此可以得到三种构造高度平衡二叉搜索树的方法。

  • 每次都将根结点值取为中间位置左边的下标处的值。

  • 每次都将根结点值取为中间位置右边的下标处的值。

  • 每次随机将根结点值取为中间位置左边或右边的下标处的值。

证明

为了证明上述构造高度平衡二叉搜索树的方法的正确性,需要证明:如果两个高度平衡二叉搜索树的结点数之差的绝对值不超过 1 1 1,则这两个高度平衡二叉搜索树的高度之差的绝对值不超过 1 1 1

h ( n ) h(n) h(n) 表示有 n n n 个结点的高度平衡二叉搜索树的高度,其中 n ≥ 1 n \ge 1 n1,规定 h ( 1 ) = 0 h(1) = 0 h(1)=0 h ( 2 ) = h ( 3 ) = 1 h(2) = h(3) = 1 h(2)=h(3)=1,则对于 1 ≤ n ≤ 3 1 \le n \le 3 1n3 h ( n ) = ⌊ log ⁡ n ⌋ h(n) = \lfloor \log n \rfloor h(n)=logn

n ≥ 4 n \ge 4 n4 时,假设对于任意 1 ≤ m < n 1 \le m < n 1m<n 都有 h ( m ) = ⌊ log ⁡ m ⌋ h(m) = \lfloor \log m \rfloor h(m)=logm,需要证明 h ( n ) = ⌊ log ⁡ n ⌋ h(n) = \lfloor \log n \rfloor h(n)=logn

  • n n n 是奇数时,令 n = 2 k + 1 n = 2k + 1 n=2k+1,其中 k ≥ 1 k \ge 1 k1,则根结点的左子树和右子树各有 k k k 个结点。由于 k < n k < n k<n,因此 h ( k ) = ⌊ log ⁡ k ⌋ h(k) = \lfloor \log k \rfloor h(k)=logk 已知,此时 h ( n ) = h ( k ) + 1 = ⌊ log ⁡ k ⌋ + 1 h(n) = h(k) + 1 = \lfloor \log k \rfloor + 1 h(n)=h(k)+1=logk+1。由于 n = 2 k + 1 n = 2k + 1 n=2k+1,因此 n − 1 = 2 k n - 1 = 2k n1=2k log ⁡ ( n − 1 ) = log ⁡ 2 k = log ⁡ k + 1 \log (n - 1) = \log 2k = \log k + 1 log(n1)=log2k=logk+1,取整得 ⌊ log ⁡ ( n − 1 ) ⌋ = ⌊ log ⁡ k ⌋ + 1 \lfloor \log (n - 1) \rfloor = \lfloor \log k \rfloor + 1 log(n1)⌋=logk+1。由于 n n n 是奇数,因此 ⌊ log ⁡ n ⌋ = ⌊ log ⁡ ( n − 1 ) ⌋ \lfloor \log n \rfloor = \lfloor \log (n - 1) \rfloor logn=log(n1)⌋ ⌊ log ⁡ n ⌋ = ⌊ log ⁡ k ⌋ + 1 \lfloor \log n \rfloor = \lfloor \log k \rfloor + 1 logn=logk+1 h ( n ) = ⌊ log ⁡ n ⌋ h(n) = \lfloor \log n \rfloor h(n)=logn

  • n n n 是偶数时,令 n = 2 k + 2 n = 2k + 2 n=2k+2,其中 k ≥ 1 k \ge 1 k1,则根结点的左子树和右子树分别有 k k k 个结点和 k + 1 k + 1 k+1 个结点。由于 k + 1 < n k + 1 < n k+1<n,因此 h ( k + 1 ) = ⌊ log ⁡ ( k + 1 ) ⌋ h(k + 1) = \lfloor \log (k + 1) \rfloor h(k+1)=log(k+1)⌋ 已知,此时 h ( n ) = h ( k + 1 ) + 1 = ⌊ log ⁡ ( k + 1 ) ⌋ + 1 h(n) = h(k + 1) + 1 = \lfloor \log (k + 1) \rfloor + 1 h(n)=h(k+1)+1=log(k+1)⌋+1。由于 n = 2 k + 2 = 2 ( k + 1 ) n = 2k + 2 = 2(k + 1) n=2k+2=2(k+1),因此 log ⁡ n = log ⁡ 2 ( k + 1 ) = log ⁡ ( k + 1 ) + 1 \log n = \log 2(k + 1) = \log (k + 1) + 1 logn=log2(k+1)=log(k+1)+1,取整得 ⌊ log ⁡ n ⌋ = ⌊ log ⁡ ( k + 1 ) ⌋ + 1 \lfloor \log n \rfloor = \lfloor \log (k + 1) \rfloor + 1 logn=log(k+1)⌋+1 h ( n ) = ⌊ log ⁡ n ⌋ h(n) = \lfloor \log n \rfloor h(n)=logn

因此对于任意正整数 n n n,都有 h ( n ) = ⌊ log ⁡ n ⌋ h(n) = \lfloor \log n \rfloor h(n)=logn。由于任意两个相邻正整数的对数之差一定不超过 1 1 1,因此当 n ≥ 2 n \ge 2 n2 时,一定有 h ( n ) − h ( n − 1 ) ≤ 1 h(n) - h(n - 1) \le 1 h(n)h(n1)1

代码

下面的代码为每次都将根结点值取为中间位置左边的下标处的值的做法。

class Solution {
    public TreeNode sortedArrayToBST(int[] nums) {
        return createBST(nums, 0, nums.length - 1);
    }

    public TreeNode createBST(int[] nums, int start, int end) {
        if (start > end) {
            return null;
        }
        int mid = (end - start) / 2 + start;
        return new TreeNode(nums[mid], createBST(nums, start, mid - 1), createBST(nums, mid + 1, end));
    }
}

下面的代码为每次都将根结点值取为中间位置右边的下标处的值的做法。

class Solution {
    public TreeNode sortedArrayToBST(int[] nums) {
        return createBST(nums, 0, nums.length - 1);
    }

    public TreeNode createBST(int[] nums, int start, int end) {
        if (start > end) {
            return null;
        }
        int mid = (end - start + 1) / 2 + start;
        return new TreeNode(nums[mid], createBST(nums, start, mid - 1), createBST(nums, mid + 1, end));
    }
}

下面的代码为每次随机将根结点值取为中间位置左边或右边的下标处的值的做法。

class Solution {
    public TreeNode sortedArrayToBST(int[] nums) {
        return createBST(nums, 0, nums.length - 1);
    }

    public TreeNode createBST(int[] nums, int start, int end) {
        if (start > end) {
            return null;
        }
        int mid = (end - start + (int) (Math.random() * 2)) / 2 + start;
        return new TreeNode(nums[mid], createBST(nums, start, mid - 1), createBST(nums, mid + 1, end));
    }
}

复杂度分析

  • 时间复杂度: O ( n ) O(n) O(n),其中 n n n 是数组 nums \textit{nums} nums 的长度。每个元素都被访问一次。

  • 空间复杂度: O ( log ⁡ n ) O(\log n) O(logn),其中 n n n 是数组 nums \textit{nums} nums 的长度。空间复杂度主要是递归调用的栈空间,由于构造的是高度平衡二叉搜索树,因此递归调用栈的深度是 O ( log ⁡ n ) O(\log n) O(logn)。注意返回值不计入空间复杂度。

  • 25
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

伟大的车尔尼

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值