前言
本文是二叉树篇刷题教程中的倒数第二道。加油。继续。
记录 六十一【108.将有序数组转换为二叉搜索树】
一、题目阅读
给你一个整数数组 nums ,其中元素已经按 升序 排列,请你将其转换为一棵 平衡 二叉搜索树。
示例 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,null,3] 和 [3,1] 都是高度平衡二叉搜索树。
提示:
1 <= nums.length <= 10^4
-10^4 <= nums[i] <= 10^4
nums 按 严格递增 顺序排列
二、尝试实现
思路
- 先理解题目:把输入的一个有序递增数组转换成平衡二叉搜索树。平衡:任何节点的左右子树高度差不超过1;二叉搜索树:节点大小关系满足左子树内节点值< 中间节点 < 右子树内节点值。感觉好复杂,再想想。
- 让构建一个二叉树:记录 五十【106.从中序与后序遍历序列构造二叉树】和【105.从前序与中序遍历序列构造二叉树】学过如此构造二叉树,是通过中间节点的位置去划分数组,直到数组中某一部分是左子树,哪一部分是右子树。这里也需要找中间节点 ,这样递归实现就可以cur->left和cur->right。
- 只给了一个数组,怎么确定中间节点呢?但是题目说平衡:把数组正中间的值当作中间节点,均分输入数组nums,左半边小于中间节点,右半边大于中间节点(二叉搜索树get);而且重复操作构建左右子树,每个节点的左右子树节点个数一样,应该可以满足平衡。思路有了,开始实现:
- 递归函数参数:输入的nums;
- 递归函数返回值:子树的根节点TreeNode*。向上一层返回创建的子树,在上面用left或者right接住返回值(很多次见到)。
- 递归函数终止条件:nums空——说明没有孩子,return nullptr。
- 递归函数逻辑:
- 找到传入数组正中间的元素,新建根节点:nums[nums.size() /2];
- 左子树=nums平分的前半段;
- 右子树=nums平分的后半段。
- 如下图,结合文字理解。
代码实现
以下代码实现中几个点:
- 因为想借助给的主函数实现递归,所以没去定义一个新函数。主函数只有一个参数nums,所以递归的时候,其实是建了新数组,开辟空间。不是用下标去控制原来的那一个数组。
- 传递区间的时候,遵循左闭右开的区间。因为vector构造函数就是左闭右开。
class Solution {
public:
TreeNode* sortedArrayToBST(vector<int>& nums) {
//终止条件
if(nums.size() == 0) return nullptr;
//中间节点,用nums的正中间的值作为父节点。
TreeNode* cur = new TreeNode(nums[nums.size()/2]);
//构建左子树
vector<int> leftsub(nums.begin(),nums.begin()+nums.size()/2 );//左闭右开
cur->left = sortedArrayToBST(leftsub);
//构建右子树
vector<int> rightsub(nums.begin()+nums.size()/2+1,nums.end());
cur->right = sortedArrayToBST(rightsub);
return cur;
}
};
三、参考学习
学习内容
- 思路一致。
- 对比参考代码【递归法】:参考给的是下标控制同一个数组,没有额外开辟空间;同时传递区间使用左闭右闭。
- 迭代法:用循环模拟递归的过程。参考代码:使用了三个队列:一个队列放子树的根节点;一个队列放该节点对应的区间左下标;一个队列放该节点对应的区间右下标。可以看成是层序遍历的顺序去构建。
总结
记录 五十【106.从中序与后序遍历序列构造二叉树】和【105.从前序与中序遍历序列构造二叉树】同思路,用中间节点切分数组即可。保持循环不变量。