文章目录
- 题目
- 标题和出处
- 难度
- 题目描述
- 要求
- 示例
- 数据范围
- 解法
- 思路和算法
- 证明
- 代码
- 复杂度分析
题目
标题和出处
标题:将有序数组转换为二叉搜索树
出处:108. 将有序数组转换为二叉搜索树
难度
4 级
题目描述
要求
给定整数数组 nums \texttt{nums} nums,其中元素已经按升序排列,将其转换为高度平衡二叉搜索树。
高度平衡二叉树满足每个结点的左右子树的高度差的绝对值不超过 1 \texttt{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] 也将被视为正确答案:
示例 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} 1≤nums.length≤104
- -10 4 ≤ nums[i] ≤ 10 4 \texttt{-10}^\texttt{4} \le \texttt{nums[i]} \le \texttt{10}^\texttt{4} -104≤nums[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,mid−1] 和 [ mid + 1 , end ] [\textit{mid} + 1, \textit{end}] [mid+1,end] 创建根结点的左子树和右子树。
当 start ≤ end \textit{start} \le \textit{end} start≤end 时, 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 n≥1,规定 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 1≤n≤3 有 h ( n ) = ⌊ log n ⌋ h(n) = \lfloor \log n \rfloor h(n)=⌊logn⌋。
当 n ≥ 4 n \ge 4 n≥4 时,假设对于任意 1 ≤ m < n 1 \le m < n 1≤m<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 k≥1,则根结点的左子树和右子树各有 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 n−1=2k, log ( n − 1 ) = log 2 k = log k + 1 \log (n - 1) = \log 2k = \log k + 1 log(n−1)=log2k=logk+1,取整得 ⌊ log ( n − 1 ) ⌋ = ⌊ log k ⌋ + 1 \lfloor \log (n - 1) \rfloor = \lfloor \log k \rfloor + 1 ⌊log(n−1)⌋=⌊logk⌋+1。由于 n n n 是奇数,因此 ⌊ log n ⌋ = ⌊ log ( n − 1 ) ⌋ \lfloor \log n \rfloor = \lfloor \log (n - 1) \rfloor ⌊logn⌋=⌊log(n−1)⌋, ⌊ 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 k≥1,则根结点的左子树和右子树分别有 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 n≥2 时,一定有 h ( n ) − h ( n − 1 ) ≤ 1 h(n) - h(n - 1) \le 1 h(n)−h(n−1)≤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)。注意返回值不计入空间复杂度。