IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 数据结构与算法 -> 【每日力扣31】将有序数组转换为二叉搜索树 -> 正文阅读

[数据结构与算法]【每日力扣31】将有序数组转换为二叉搜索树

一、题目[LeetCode-108]

给你一个整数数组 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 <= 10^4
  • -10^4?<= nums[i] <= 10^4
  • nums?按?严格递增?顺序排列

?

二、思路

递归:中序遍历转为层次遍历

1)首先处理数组nums,将其严格递增的排列顺序更改为最终所求平衡BST树的层次遍历顺序(即根值为nums[0],根的左儿子的值为nums[1],根的右儿子的值为nums[2],根的左儿子的左儿子的值为nums[3])。

这里使用递归的方法。先找出数组nums的中间数nums[nums.size()/2](该数将作为生成的数的根节点),将其赋值给新数组的第一位newNums[0],然后递归处理子区间nums[0, middle - 1]和nums[middle + 1, nums.size()-1],找出的中位数分别作为newNums[1], newNums[2]。

具体流程:首先创建一个新数组newNum,大小拓展为刚好超过num.size()的2^i-1(这样是最近的完全二叉树的个数),多出来的位置便是二叉树的nullptr,这样有利于后续递归子函数调用的统一化。然后编写递归子函数,其中有一个参数index,意为插入到newNum中的位置。它的递归方式为:对于当前节点在newNum中的位置为index,则左儿子的位置为2index+1,右儿子位置为2index+2。

2)将nums转化为二叉搜索树。这里再次使用递归的方法。对于任意节点nums[i],其左右子节点值分别为nums[2i+1],nums[2i+2](在不越界的情况下)。利用这个性质我们便可进行递归构造二叉搜索树。

/**
?*?Definition?for?a?binary?tree?node.
?*?struct?TreeNode?{
?*?????int?val;
?*?????TreeNode?*left;
?*?????TreeNode?*right;
?*?????TreeNode()?:?val(0),?left(nullptr),?right(nullptr)?{}
?*?????TreeNode(int?x)?:?val(x),?left(nullptr),?right(nullptr)?{}
?*?????TreeNode(int?x,?TreeNode?*left,?TreeNode?*right)?:?val(x),?left(left),?right(right)?{}
?*?};
?*/
class?Solution?{
public:
????const?int?INTOFNULL?=?10001;
????TreeNode*?sortedArrayToBST(vector<int>&?nums)?{
????????vector<int>?newNums?=?moveArray(nums);
????????return?creatTreeNodes(newNums,?0);
????}
????vector<int>?moveArray(vector<int>&?nums){//创建新数组newNum,里面是将nums的元素由递增排序变为层次遍历的排序。(大小拓展到恰好大于nums.size()的完全二叉树大小,多出来的位置即是nullptr)并在这里调用重载递归子函数。
????????int?n?=?nums.size();
????????int?i?=?1;
????????while(pow(2,i)-1?<?n)
????????????i++;
????????vector<int>?newNums(pow(2,i)-1);//找出离n最近的恰好大于n的完全二叉树的节点个数,作为新数组的size
????????moveArray(nums,?newNums,?0,?nums.size()-1,?0);//重载递归子函数
????????return?newNums;
????}
????void?moveArray(vector<int>&?nums,?vector<int>&?newNums,?int?left,?int?right,?int?index){
????????if(index?>=?newNums.size())//递归基————如果index越界了直接返回
????????????return;
????????if(left?>?right)//当左边界大于右边界时,调用它的上一个函数是middle==left或middle==right,到这里是空节点了
????????{
????????????newNums[index]?=?INTOFNULL;//表明newNums的该位置在二叉树中是nullptr
????????????return;
????????}
????????int?middle?=?(left?+?right)/2;
????????newNums[index]?=?nums[middle];//将中间值插入新数组的对应位置index
????????moveArray(nums,?newNums,?left,?middle-1,?index*2+1);//递归调用。这里左儿子的index便是2index+1
????????moveArray(nums,?newNums,?middle+1,?right,?index*2+2);//递归调用。这里右儿子的index便是2index+2
????}
????TreeNode*?creatTreeNodes(vector<int>&?nums,?int?index){
????????if(index?>=?nums.size()?||?nums[index]?==?INTOFNULL)
????????????return?nullptr;
????????return?new?TreeNode(nums[index],?creatTreeNodes(nums,?index*2+1),?creatTreeNodes(nums,?index*2+2));
????}
};

三、官方题解(来源:力扣(LeetCode))?

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

选择中间位置左边的数字作为根节点,则根节点的下标为 mid=(left+right)/2,此处的除法为整数除法。

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;
    }
};

作者:LeetCode-Solution
链接:https://leetcode-cn.com/problems/convert-sorted-array-to-binary-search-tree/solution/jiang-you-xu-shu-zu-zhuan-huan-wei-er-cha-sou-s-33/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

复杂度分析

  • 时间复杂度:O(n),其中 n 是数组的长度。每个数字只访问一次。
  • 空间复杂度:O(logn),其中 n 是数组的长度。空间复杂度不考虑返回值,因此空间复杂度主要取决于递归栈的深度,递归栈的深度是 O(logn)。

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

选择中间位置右边的数字作为根节点,则根节点的下标为 mid=(left+right+1)/2,此处的除法为整数除法。

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;
    }
};

作者:LeetCode-Solution
链接:https://leetcode-cn.com/problems/convert-sorted-array-to-binary-search-tree/solution/jiang-you-xu-shu-zu-zhuan-huan-wei-er-cha-sou-s-33/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

复杂度分析

  • 时间复杂度:O(n),其中 n 是数组的长度。每个数字只访问一次。
  • 空间复杂度:O(logn),其中 n 是数组的长度。空间复杂度不考虑返回值,因此空间复杂度主要取决于递归栈的深度,递归栈的深度是 O(logn)。

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

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

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-Solution
链接:https://leetcode-cn.com/problems/convert-sorted-array-to-binary-search-tree/solution/jiang-you-xu-shu-zu-zhuan-huan-wei-er-cha-sou-s-33/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

复杂度分析

  • 时间复杂度:O(n),其中 n 是数组的长度。每个数字只访问一次。
  • 空间复杂度:O(logn),其中 n 是数组的长度。空间复杂度不考虑返回值,因此空间复杂度主要取决于递归栈的深度,递归栈的深度是 O(logn)。

四、学习心得

①二叉搜索树BST的中序遍历结果即为一个严格递增的数组

因此本题其实给的即是所求二叉搜索树的中序遍历结果。对于一个二叉树的中序遍历结果:

  • 中序遍历结果不能唯一地确定二叉搜索树
  • 中序遍历结果不能唯一地确定平衡二叉搜索树,但是对于每一棵子树的创建时,根从中序数组区间中的选取位置各有最多两种选择(对于子树的节点个数(中序遍历数组对应的区间长度)为奇数时,根只有一个选择——数组区间中间值;为偶数时,根有两种选择——中间位置的左边值和中间位置的右边值)

因此,官方解法中,一种思路给出了三种可供选择的根选取的方法。

②数组越界问题

在递归地选定数组区间(涉及到参数left和right)时,递归基为left恰好比right大于1,这时应当在递归子函数的前面先用if语句写出对应的特殊操作——返回null指针,否则将会越界。

  数据结构与算法 最新文章
【力扣106】 从中序与后续遍历序列构造二叉
leetcode 322 零钱兑换
哈希的应用:海量数据处理
动态规划|最短Hamilton路径
华为机试_HJ41 称砝码【中等】【menset】【
【C与数据结构】——寒假提高每日练习Day1
基础算法——堆排序
2023王道数据结构线性表--单链表课后习题部
LeetCode 之 反转链表的一部分
【题解】lintcode必刷50题<有效的括号序列
上一篇文章      下一篇文章      查看所有文章
加:2022-01-25 10:49:54  更:2022-01-25 10:51:49 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/26 19:24:32-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码