数据结构与算法之深入解析“将有序数组转换为二叉搜索树”的求解思路与算法示例

Posted Serendipity·y

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了数据结构与算法之深入解析“将有序数组转换为二叉搜索树”的求解思路与算法示例相关的知识,希望对你有一定的参考价值。

一、题目要求

  • 给你一个整数数组 nums ,其中元素已经按升序排列,请你将其转换为一棵高度平衡 二叉搜索树
  • 高度平衡二叉树是一棵满足「每个节点的左右两个子树的高度差的绝对值不超过 1 」的二叉树。
  • 示例 1:

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

  • 示例 2:
输入:nums = [1,3]
输出:[3,1]
解释:[1,3][3,1] 都是高度平衡二叉搜索树。
  • 提示:
    • 1 <= nums.length <= 104
    • -104 <= nums[i] <= 104
    • nums 按严格递增顺序排列。

二、思路分析

  • 二叉搜索树的中序遍历是升序序列,题目给定的数组是按照升序排序的有序数组,因此可以确保数组是二叉搜索树的中序遍历序列。
  • 给定二叉搜索树的中序遍历,是否可以唯一地确定二叉搜索树?答案是否定的,如果没有要求二叉搜索树的高度平衡,则任何一个数字都可以作为二叉搜索树的根节点,因此可能的二叉搜索树有多个。以下 BST 中序遍历结果均为 [-10, -3, 0, 5, 9]:
  • 如果增加一个限制条件,即要求二叉搜索树的高度平衡,是否可以唯一地确定二叉搜索树?答案仍然是否定的。以下 BST 中序遍历结果均为 [-10, -3, 0, 5, 9]:

  • 直观地看,可以选择中间数字作为二叉搜索树的根节点,这样分给左右子树的数字个数相同或只相差 1,可以使得树保持平衡。如果数组长度是奇数,则根节点的选择是唯一的,如果数组长度是偶数,则可以选择中间位置左边的数字作为根节点或者选择中间位置右边的数字作为根节点,选择不同的数字作为根节点则创建的平衡二叉搜索树也是不同的。
  • 每次选择中间元素作为根节点 [-10, -3, 0, 5, 9]:

  • 确定平衡二叉搜索树的根节点之后,其余的数字分别位于平衡二叉搜索树的左子树和右子树中,左子树和右子树分别也是平衡二叉搜索树,因此可以通过递归的方式创建平衡二叉搜索树。递归的基准情形是平衡二叉搜索树不包含任何数字,此时平衡二叉搜索树为空。
  • 在给定中序遍历序列数组的情况下,每一个子树中的数字在数组中一定是连续的,因此可以通过数组下标范围确定子树包含的数字,下标范围记为 [left,right]。对于整个中序遍历序列,下标范围从 left=0 到 right=nums.length−1。当 left>right 时,平衡二叉搜索树为空。

三、求解算法

① 中序遍历,总是选择中间位置左边的数字作为根节点

  • 选择中间位置左边的数字作为根节点,则根节点的下标为 mid=(left+right)/2,此处的除法为整数除法,总是选择中间元素左边的数字作为根节点 [-10, -3, 0, 5, 9]:

  • Java 示例:
class Solution 
    public TreeNode sortedArrayToBST(int[] nums) 
        return helper(nums, 0, nums.length - 1);
    

    public TreeNode helper(int[] nums, int left, int right) 
        if (left > right) 
            return null;
        

        // 总是选择中间位置左边的数字作为根节点
        int mid = (left + right) / 2;

        TreeNode root = new TreeNode(nums[mid]);
        root.left = helper(nums, left, mid - 1);
        root.right = helper(nums, mid + 1, right);
        return root;
    

  • C++ 示例:
class Solution 
public:
    TreeNode* sortedArrayToBST(vector<int>& nums) 
        return helper(nums, 0, nums.size() - 1);
    

    TreeNode* helper(vector<int>& nums, int left, int right) 
        if (left > right) 
            return nullptr;
        

        // 总是选择中间位置左边的数字作为根节点
        int mid = (left + right) / 2;

        TreeNode* root = new TreeNode(nums[mid]);
        root->left = helper(nums, left, mid - 1);
        root->right = helper(nums, mid + 1, right);
        return root;
    
;

② 中序遍历,总是选择中间位置右边的数字作为根节点

  • 选择中间位置右边的数字作为根节点,则根节点的下标为 mid=(left+right+1)/2,此处的除法为整数除法。总是选择中间元素右边的数字作为根节点 [-10, -3, 0, 5, 9]:

  • Java 示例:
class Solution 
    public TreeNode sortedArrayToBST(int[] nums) 
        return helper(nums, 0, nums.length - 1);
    

    public TreeNode helper(int[] nums, int left, int right) 
        if (left > right) 
            return null;
        

        // 总是选择中间位置右边的数字作为根节点
        int mid = (left + right + 1) / 2;

        TreeNode root = new TreeNode(nums[mid]);
        root.left = helper(nums, left, mid - 1);
        root.right = helper(nums, mid + 1, right);
        return root;
    

  • C++ 示例:
class Solution 
public:
    TreeNode* sortedArrayToBST(vector<int>& nums) 
        return helper(nums, 0, nums.size() - 1);
    

    TreeNode* helper(vector<int>& nums, int left, int right) 
        if (left > right) 
            return nullptr;
        

        // 总是选择中间位置右边的数字作为根节点
        int mid = (left + right + 1) / 2;

        TreeNode* root = new TreeNode(nums[mid]);
        root->left = helper(nums, left, mid - 1);
        root->right = helper(nums, mid + 1, right);
        return root;
    
;

③ 中序遍历,选择任意一个中间位置数字作为根节点

  • 选择任意一个中间位置数字作为根节点,则根节点的下标为 mid=(left+right)/2 和 mid=(left+right+1)/2 两者中随机选择一个,此处的除法为整数除法。选择任意一个中间位置作为根节点 [-10, -3, 0, 5, 9]:

  • Java 示例:
class Solution 
    Random rand = new Random();

    public TreeNode sortedArrayToBST(int[] nums) 
        return helper(nums, 0, nums.length - 1);
    

    public TreeNode helper(int[] nums, int left, int right) 
        if (left > right) 
            return null;
        

        // 选择任意一个中间位置数字作为根节点
        int mid = (left + right + rand.nextInt(2)) / 2;

        TreeNode root = new TreeNode(nums[mid]);
        root.left = helper(nums, left, mid - 1);
        root.right = helper(nums, mid + 1, right);
        return root;
    

  • C++ 示例:
class Solution 
public:
    TreeNode* sortedArrayToBST(vector<int>& nums) 
        return helper(nums, 0, nums.size() - 1);
    

    TreeNode* helper(vector<int>& nums, int left, int right) 
        if (left > right) 
            return nullptr;
        

        // 选择任意一个中间位置数字作为根节点
        int mid = (left + right + rand() % 2) / 2;

        TreeNode* root = new TreeNode(nums[mid]);
        root->left = helper(nums, left, mid - 1);
        root->right = helper(nums, mid + 1, right);
        return root;
    
;

以上是关于数据结构与算法之深入解析“将有序数组转换为二叉搜索树”的求解思路与算法示例的主要内容,如果未能解决你的问题,请参考以下文章

算法系列——将有序数组转换为二叉搜索树

算法系列——将有序数组转换为二叉搜索树

LeetCode 108. 将有序数组转换为二叉搜索树

⭐算法入门⭐《二叉树 - 二叉搜索树》简单03 —— LeetCode 108. 将有序数组转换为二叉搜索树

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

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