描述
题目描述
给出一个升序排序的数组,将其转化为平衡二叉搜索树(BST).
示例
输入:[-1,0,1,2] 返回值:{1,0,2,-1}
知识点:二叉搜索树,排序,数组,二分查找
难度:⭐⭐
题解
解题思路
对于有序数组,首先应该想到用二分法,接着是构造二叉搜索树的过程,应该想到用分治法,分别对左右子树进行构建后,再合并,而这个类似先构造左子树再构造右子树的过程,可以通过递归解决
方法一:递归+二分查找
图解
算法流程:
- 定义递归函数功能:找出数组num的[left,right]区间的中间偏大的元素
- 二分法:因为递归是先找出并设置左子结点,因此mid要取的中间元素的偏大的中间元素
- 向左递归,继续寻找左区间的根节点
- 向右递归,继续寻找右区间的根节点
Java 版本代码如下:
import java.util.*; public class Solution { public TreeNode sortedArrayToBST (int[] num) { if(num == null || num.length == 0) { return null; } return recur(num, 0, num.length - 1); } // 定义递归函数功能:找出数组num的[left,right]区间的中间偏大的元素 private TreeNode recur(int[] num, int left, int right) { // 递归终止条件 if(left > right) { return null; } // 因为递归是先找出并设置左子结点,因此mid要取的中间元素的偏大的中间元素 // 例如[-1,0,1,2],如果没有+1,则根节点会是中甲元素偏小的0,最后最大值2将是左子节点,不满足二叉搜索树特性 int mid = left + ((right - left + 1) >> 1); TreeNode root = new TreeNode(num[mid]); // 向左递归,继续寻找左区间的根节点 root.left = recur(num, left, mid - 1); // 向右递归,继续寻找右区间的根节点 root.right = recur(num, mid + 1, right); return root; } }
Python 版本代码如下:
class Solution: def sortedArrayToBST(self , num ): if len(num) == 0: return None if len(num) == 1: return TreeNode(num[0]) # 中间元素为当前区间的根节点 idx = len(num) // 2 node = TreeNode(num[idx]) # 左右区间递归,寻找左右区间的根节点 node.left = self.sortedArrayToBST(num[:idx]) node.right = self.sortedArrayToBST(num[idx+1:]) return node
复杂度分析:
时间复杂度 O(N):尽管采用了二分法,但递归构造树时,递归经过每个结点,N为数组长度,及二叉搜索树结点个数
空间复杂度 O(N):结果集用到了TreeNode保存最终结果,N为节点数
总结:遇到有序数组,一定要最先想到二分法,还能降低复杂度
方法二:迭代
算法流程:
- 维护两个stack,一个保存结果,一个保存每次迭代的左右分界点
- 迭代每个数组每个元素,先获取当前区间,以及当前区间根节点在数组的位置
- 构造当前结点,接着根据左右区间,分别构建左右子节点
- 每次都要更新保存左右分界点的stack
Java 版本代码如下:
import java.util.*; public class Solution { public TreeNode sortedArrayToBST (int[] num) { if(num == null || num.length == 0) { return null; } // 保存结果 Stack<TreeNode> res = new Stack<>(); // 保存左右分界点 Stack<Integer> stack = new Stack<>(); // 初始化根节点 TreeNode root = new TreeNode(0); res.push(root); stack.push(0); stack.push(num.length - 1); while(res.size() > 0) { // 获取当前区间 int right = stack.pop(); int left = stack.pop(); // 当前区间根节点在数组的位置 int mid = left + ((right - left + 1) >> 1); TreeNode node = res.pop(); // 当前区间的根节点赋值 node.val = num[mid]; // 左区间,构建左子节点 if(left <= mid - 1) { node.left = new TreeNode(0); // 初始化左子节点,为了下次构建二叉树 res.push(node.left); stack.push(left); // 缩小为左区间 stack.push(mid - 1); } // 右区间,构建右子节点 if(right >= mid + 1) { node.right = new TreeNode(0); res.push(node.right); stack.push(mid + 1); stack.push(right); } } return root; } }
复杂度分析:
时间复杂度 O(N):需要遍历N个结点
空间复杂度 O(logN):栈所需的额外空间