给「代码随想录」一个星标吧!

构造二叉搜索树,一不小心就平衡了

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

将一个按照升序排列的有序数组,转换为一棵高度平衡二叉搜索树。

本题中,一个高度平衡二叉树是指一个二叉树每个节点 的左右两个子树的高度差的绝对值不超过 1。

示例:

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

思路

题目中说要转换为一棵高度平衡二叉搜索树。这和转换为一棵普通二叉搜索树有什么差别呢?

其实这里不用强调平衡二叉搜索树,数组构造二叉树,构成平衡树是自然而然的事情,因为大家默认都是从数组中间位置取值作为节点元素,一般不会随机取,「所以想构成不平衡的二叉树是自找麻烦」

在二叉树:构造二叉树登场!和二叉树:构造一棵最大的二叉树中其实已经讲过了,如果根据数组构造一颗二叉树。

「本质就是寻找分割点,分割点作为当前节点,然后递归左区间和右区间」

本题其实要比二叉树:构造二叉树登场! 和 二叉树:构造一棵最大的二叉树简单一些,因为有序数组构造二叉搜索树,寻找分割点就比较容易了。

分割点就是数组中间位置的节点。

那么为问题来了,如果数组长度为偶数,中间节点有两个,取哪一个?

取哪一个都可以,只不过构成了不同的平衡二叉搜索树。

例如:输入:[-10,-3,0,5,9]

如下两棵树,都是这个数组的平衡二叉搜索树:

如果要分割的数组长度为偶数的时候,中间元素为两个,是取左边元素  就是树1,取右边元素就是树2。

「这也是题目中强调答案不是唯一的原因。理解这一点,这道题目算是理解到位了」

递归

递归三部曲:

  • 确定递归函数返回值及其参数

删除二叉树节点,增加二叉树节点,都是用递归函数的返回值来完成,这样是比较方便的。

相信大家如果仔细看了二叉树:搜索树中的插入操作和二叉树:搜索树中的删除操作,一定会对递归函数返回值的作用深有感触。

那么本题要构造二叉树,依然用递归函数的返回值来构造中节点的左右孩子。

再来看参数,首先是传入数组,然后就是左下表left和右下表right,我们在二叉树:构造二叉树登场!中提过,在构造二叉树的时候尽量不要重新定义左右区间数组,而是用下表来操作原数组。

所以代码如下:

// 左闭右闭区间[left, right]TreeNode* traversal(vector& nums, int left, int right) 

这里注意,「我这里定义的是左闭右闭区间,在不断分割的过程中,也会坚持左闭右闭的区间,这又涉及到我们讲过的循环不变量」

在二叉树:构造二叉树登场!,35.搜索插入位置 和59.螺旋矩阵II都详细讲过循环不变量。

  • 确定递归终止条件

这里定义的是左闭右闭的区间,所以当区间 left > right的时候,就是空节点了。

代码如下:

if (left > right) return nullptr;
  • 确定单层递归的逻辑

首先取数组中间元素的位置,不难写出int mid = (left + right) / 2;「这么写其实有一个问题,就是数值越界,例如left和right都是最大int,这么操作就越界了,在二分法中尤其需要注意!」

所以可以这么写:int mid = left + ((right - left) / 2);

但本题leetcode的测试数据并不会越界,所以怎么写都可以。但需要有这个意识!

取了中间位置,就开始以中间位置的元素构造节点,代码:TreeNode* root = new TreeNode(nums[mid]);

接着划分区间,root的左孩子接住下一层左区间的构造节点,右孩子接住下一层右区间构造的节点。

最后返回root节点,单层递归整体代码如下:

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

这里int mid = left + ((right - left) / 2);的写法相当于是如果数组长度为偶数,中间位置有两个元素,取靠左边的。

  • 递归整体代码如下:
class Solution {private:    TreeNode* traversal(vector& nums, int left, int right) {if (left > right) return nullptr;        int mid = left + ((right - left) / 2);         TreeNode* root = new TreeNode(nums[mid]);        root->left = traversal(nums, left, mid - 1);        root->right = traversal(nums, mid + 1, right);return root;    }public:    TreeNode* sortedArrayToBST(vector& nums) {        TreeNode* root = traversal(nums, 0, nums.size() - 1);return root;    }};

「注意:在调用traversal的时候为什么传入的left和right为什么是0和nums.size() - 1,因为定义的区间为左闭右闭」

迭代法

迭代法可以通过三个队列来模拟,一个队列放遍历的节点,一个队列放左区间下表,一个队列放右区间下表。

模拟的就是不断分割的过程,C++代码如下:(我已经详细注释)

class Solution {public:    TreeNode* sortedArrayToBST(vector& nums) {if (nums.size() == 0) return nullptr;        TreeNode* root = new TreeNode(0);   // 初始根节点        queue nodeQue;           // 放遍历的节点        queue leftQue;                 // 保存左区间下表        queue rightQue;                // 保存右区间下表        nodeQue.push(root);                 // 根节点入队列        leftQue.push(0);                    // 0为左区间下表初始位置        rightQue.push(nums.size() - 1);     // nums.size() - 1为右区间下表初始位置while (!nodeQue.empty()) {            TreeNode* curNode = nodeQue.front();            nodeQue.pop();            int left = leftQue.front(); leftQue.pop();            int right = rightQue.front(); rightQue.pop();            int mid = left + ((right - left) / 2);            curNode->val = nums[mid];       // 将mid对应的元素给中间节点if (left <= mid - 1) {          // 处理左区间                curNode->left = new TreeNode(0);                nodeQue.push(curNode->left);                leftQue.push(left);                rightQue.push(mid - 1);            }if (right >= mid + 1) {         // 处理右区间                curNode->right = new TreeNode(0);                nodeQue.push(curNode->right);                leftQue.push(mid + 1);                rightQue.push(right);            }        }return root;    }};

总结

「在二叉树:构造二叉树登场! 和 二叉树:构造一棵最大的二叉树之后,我们顺理成章的应该构造一下二叉搜索树了,一不小心还是一棵平衡二叉搜索树」

其实思路也是一样的,不断中间分割,然后递归处理左区间,右区间,也可以说是分治。

此时相信大家应该对通过递归函数的返回值来增删二叉树很熟悉了,这也是常规操作。

在定义区间的过程中我们又一次强调了循环不变量的重要性。

最后依然给出迭代的方法,其实就是模拟取中间元素,然后不断分割去构造二叉树的过程。

「就酱,如果对你有帮助的话,也转发给身边需要的同学吧!」

在留言区留下你的思路吧!

-------end-------

我将算法学习相关的资料已经整理到了Github :https://github.com/youngyangyang04/leetcode-master,里面还有leetcode刷题攻略、各个类型经典题目刷题顺序、思维导图看一看一定会有所收获,如果给你有帮助给一个star支持一下吧!

另外因为公众号改版,时间线被打乱,一些精彩文章大家可能错过了。如果感觉这里的文章对你有帮助,赶紧给「代码随想录」加一个星标吧,方便第一时间阅读文章往期精彩回顾二叉树:修剪一棵搜索树二叉树:搜索树中的删除操作二叉树:搜索树中的插入操作二叉树:搜索树的公共祖先问题本周小结!(二叉树系列四)二叉树:公共祖先问题二叉树:我的众数是多少?二叉树:搜索树的最小绝对差二叉树:我是不是一棵二叉搜索树二叉树:二叉搜索树登场!二叉树:合并两个二叉树本周小结!(二叉树系列三)二叉树:构造一棵最大的二叉树二叉树:构造二叉树登场!「代码随想录」期待你的关注!

每天8:35准时推送一道经典算法题目,推送的每道题目都不是孤立的,而是由浅入深,环环相扣,帮你梳理算法知识脉络,轻松学算法!

刷题可以加我微信!右边为个人微信,添加时备注:「简单自我介绍」+「组队刷题」我就知道你[在看]

pta 是否完全二叉搜索树_二叉树:构造一棵搜索树相关推荐

  1. 3006基于二叉链表的二叉树最长路径的求解(附思路)

    描述 设二叉树中每个结点的元素均为一个字符,按先序遍历的顺序建立二叉链表,编写算法求出该二叉树中第一条最长的路径. 输入 多组数据.每组数据一行,为二叉树的先序序列(序列中元素为'0'时,表示该结点为 ...

  2. 3010基于二叉链表的二叉树高度的计算(附思路,WA的一种可能情况及代码)

    基于二叉链表的二叉树高度的计算 描述 设二叉树中每个结点的元素均为一个字符,按先序遍历的顺序建立二叉链表,编写递归算法计算二叉树的高度. 输入 多组数据.每组数据一行,为二叉树的前序序列(序列中元素为 ...

  3. 3010基于二叉链表的二叉树高度的计算

    描述 设二叉树中每个结点的元素均为一个字符,按先序遍历的顺序建立二叉链表,编写递归算法计算二叉树的高度. 输入 多组数据.每组数据一行,为二叉树的前序序列(序列中元素为'0'时,表示该结点为空).当输 ...

  4. 3003基于二叉链表的二叉树左右子树的交换

    描述 设二叉树中每个结点的元素均为一个字符,按先序遍历的顺序建立二叉链表,编写递归算法交换该二叉树的左右孩子. 输入 多组数据.每组数据一行,为二叉树的先序序列(序列中元素为'0'时,表示该结点为空) ...

  5. 【数据结构】基于二叉链表的二叉树结点个数的统计

    基于二叉链表的二叉树结点个数的统计 描述 设二叉树中每个结点的元素均为一个字符,按先序遍历的顺序建立二叉链表,编写三个递归算法分别对二叉树的结点(度为0.1.2)个数进行统计. 输入 多组数据.每组数 ...

  6. 基于二叉链表的二叉树高度的计算

    描述 设二叉树中每个结点的元素均为一个字符,按先序遍历的顺序建立二叉链表,编写递归算法计算二叉树的高度. 输入 多组数据.每组数据一行,为二叉树的前序序列(序列中元素为'0'时,表示该结点为空).当输 ...

  7. pta 是否完全二叉搜索树_23.二叉搜索树的后序遍历序列

    点击上方蓝字,关注并星标,和我一起学技术. 题目描述 输入一个整数数组,判断该数组是不是某二叉搜索树的后序遍历的结果.如果是则返回true,否则返回false.假设输入的数组的任意两个数字都互不相同. ...

  8. 二叉平衡树之二叉树搜索树【咱们一起手动模拟实现】

    目录 1.什么是二叉搜索树? 2.手动模拟二叉搜索树 2.1.整体代码 2.2.查找数据 2.3.插入数据 2.4.删除数据 3.性能分析 1.什么是二叉搜索树? 二叉搜索树也叫作二叉排序树,可以使一 ...

  9. 3007基于二叉链表的二叉树叶子结点到根结点的路径的求解(附DFS在树里的应用分析,思路详解)

    以下代码用到的前置知识:DFS算法 可以看这个视频~只用看这个就能懂了~ DFS深搜解决迷宫问题(原理分析+代码实现)_哔哩哔哩_bilibilihttps://www.bilibili.com/vi ...

最新文章

  1. 搭建Hadoop的Eclipse开发环境
  2. python小游戏编程实例-Python实现的弹球小游戏示例
  3. Android使用ConstraintLayout 加载RecyclerView数据显示不全
  4. Python基础入门6_文件和异常
  5. Jquery 中的CheckBox、 RadioButton、 DropDownList的取值赋值
  6. 使用matlab如何求卷积,利用MATLAB求解卷积的方法分类
  7. ArcGIS实验教程——实验二十八:统计图表(饼状图、柱状图)制作
  8. 阿里云使用经验分享——小投入大收益
  9. 【backtrader回测】隔夜持仓 VS 日内交易
  10. 眼见为实(2):介绍Windows的窗口、消息、子类化和超类化
  11. 判断一个整数是否为素数。本题要求编写程序,判断一个给定的整数是否为素数。素数就是只能被1和自身整除的正整数,1不是素数,2是素数。
  12. Zero Knowledge Proof 解密 QAP
  13. 学习《论文写作》课程的收获
  14. Invalid bound statement (not found)错误的几种解决方法
  15. 在公共服务器上搭建python虚拟环境
  16. 【Simulink】粒子群算法(PSO)整定PID参数(附代码和讲解)
  17. 百度开源RPC框架brpc
  18. 分享一个ubuntu18.04(20.04)的MacOS美化主题和下载,作者很用心也写得很详细。
  19. Golang语言全栈开发视频教程全集
  20. Cadence PCB仿真使用Allegro PCB SI电磁干扰EMI配置图文教程

热门文章

  1. 微服务架构日志集中化 安装 EFK (Fluentd ElasticSearch Kibana) 采集nginx日志
  2. Springboot微服务开发教程系列:开发入门
  3. MySQL性能优化之必备技能【推荐】
  4. leetcode 688. Knight Probability in Chessboard | 688. “马”在棋盘上的概率(dp,记忆化搜索)
  5. leetcode 299. Bulls and Cows | 299. 猜数字游戏(Java)
  6. 【Java语法】StringBuilder 可变字符序列 和普通String的对比
  7. 牛客网_PAT乙级_1029有几个PAT(25)【输入字符串并strlen计算长度】
  8. 一文弄懂EnumMap和EnumSet
  9. Hadoop机架感知
  10. 大数据图数据库之数据分片