文章目录

  • 知识回顾
  • 例题讲解
    • 1. 面试题17.12: BiNode
    • 2. 剑指Offer 33: 二叉搜索树的后续遍历序列
    • 3. Leetcode 1008: 前序遍历构造二叉搜索树
    • 4. 面试题04.09: 二叉搜索树序列

知识回顾

二叉排序树(二叉搜索树)的基础知识在之前的文章中有详细介绍,包括概念、插入、删除等基本操作,并进行了代码演示。
AVL树作为一种特殊的二叉排序树,对树高做了限制,在之前的文章中也有介绍,包括基本概念,性质,插入与删除过程中出现失衡后如何调整。并进行了代码演示。

例题讲解

1. 面试题17.12: BiNode

题目链接
题目解析:利用结构化思维,将二叉搜索树看成一个有序序列,中序遍历时记录并维护前一个遍历过的节点,让前一个节点的右子树指向当前节点,再将当前节点的左子树置空即可。

/*** Definition for a binary tree node.* struct TreeNode {*     int val;*     TreeNode *left;*     TreeNode *right;*     TreeNode(int x) : val(x), left(NULL), right(NULL) {}* };*/
class Solution {public:TreeNode *pre, *head;void inorder(TreeNode *root) {if (root == nullptr) return;inorder(root->left);if (pre == nullptr) {head = root;}else {pre->right = root;}root->left = nullptr;pre = root;inorder(root->right);return ;}TreeNode* convertBiNode(TreeNode* root) {pre = head = nullptr;inorder(root);return head;}
};

注意:本题不能简单的将左右子树分别转换为链表,再针对根节点将左右子树原地调整,因为试过如下代码:

class Solution {public:TreeNode* convertBiNode(TreeNode* root) {if (root == nullptr) return root;TreeNode *left = convertBiNode(root->left);TreeNode *right = convertBiNode(root->right);if (left == nullptr) return root;TreeNode *node = left;root->left = nullptr;while (node->right) {node = node->right;}node->right = root;return left;}
};

这样右子树的左子树会都丢失,只剩下右子树的右子树,而且答案也是错误的。为避免递归遍历的各种指针问题,中序遍历是最稳妥的。

2. 剑指Offer 33: 二叉搜索树的后续遍历序列

题目链接
题目解析:同样使用结构化思维,将后续序列数组 看成一棵二叉树,对这棵树进行中序遍历。遍历的过程中维护和记录前一个节点,看当前节点是否大于前一个节点,出现异常情况则直接返回false。

class Solution {public:int pre = -1; //遍历的前一个节点,用下标表示bool inorder(vector<int> &nums, int l, int r) {if (l > r) return true;int ind = l;while (nums[ind] < nums[r]) ind++; //找到右子树位置,这里不需要二分查找if (!inorder(nums, l, ind - 1)) return false; if (pre != -1 && nums[r] < nums[pre]) return false;pre = r;if (!inorder(nums, ind, r - 1)) return false;return true;}bool verifyPostorder(vector<int>& postorder) {return inorder(postorder, 0, postorder.size() - 1);}
};

总结:开始想的是是否能转化为二叉搜索树,但是考虑到二叉搜索树常用的结构化思维,可以直接在数组中进行中序遍历。
代码小技巧

  1. 中序遍历inorder函数直接返回值,这样不用维护是否遍历成功的全局变量,左子树或右子树遍历失败时也可以直接返回。
  2. 前一个节点用数组下标表示,这样初始化为-1,就不需要使用一个额外变量来判断当前节点是否是遍历的第一个节点。
  3. 寻找第一个大于根节点nums[r]节点的下标,这里用了直接依次比较法,而没有用二分法,因为从全局来看,这样依次比较也相当于是最终最多遍历了一遍数组。试过用二分法查找和这样依次比较查找的差异,二分法查找结果会很慢。原因应该是因为有一些不满足二叉搜索树的序列,在很早的位置就到达了搜索重点,而依次比较法会差的很快。

3. Leetcode 1008: 前序遍历构造二叉搜索树

题目链接
题目解析:和前一道题目类似,首先对于第一个值构造一棵单节点二叉树,然后往后依次找到右子树节点的位置(第一个大于根节点的值),然后对于左子树和右子树分别构造一棵二叉树即可。

/*** 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:TreeNode *dfs(vector<int> &nums, int l, int r) {if (l > r) return nullptr;TreeNode *root = new TreeNode(nums[l]);int ind = l + 1;//找到右子树的位置,这里和上一道题一样也不需要二分搜索while (ind < nums.size() && nums[ind] < nums[l]) ind++;root->left = dfs(nums, l + 1, ind - 1);root->right = dfs(nums, ind, r);return root;}TreeNode* bstFromPreorder(vector<int>& preorder) {if(preorder.size() == 0) return nullptr;TreeNode *root = dfs(preorder, 0, preorder.size() - 1);return root;}
};

代码上注意对于ind的范围判断。

4. 面试题04.09: 二叉搜索树序列

题目链接
题目解析:首先要求的序列应该满足一个基本性质,即任意一个节点必须出现在其左右子树所有节点的前面。
当获得了左子树和右子树的分别一个序列后,只需要递归的遍历两个序列,从其中任意一个序列中按从前到后的顺序取出一个数,然后继续遍历后面剩下的数字。

/*** Definition for a binary tree node.* struct TreeNode {*     int val;*     TreeNode *left;*     TreeNode *right;*     TreeNode(int x) : val(x), left(NULL), right(NULL) {}* };*/
class Solution {public:void mergeSequences(vector<int> &l, int lind,vector<int> &r, int rind, vector<int> &buff, vector<vector<int>> &ret) {/******************合并左子树的一个合法序列和右子树的一个合法序列l: 左子树的一个合法序列lind: 当前从l中按顺序取了几个数r: 右子树的一个合法序列rind: 当前从r中按顺序取了几个数buff: 缓冲数组,存储每次临时取数的结果,当l和r都取完时,将buff加到结果数组中ret: 结果数组,当l和r都取完时更新*****************/if (lind == l.size() && rind == r.size()) {ret.push_back(buff);return;}if (lind < l.size()) {buff.push_back(l[lind]);mergeSequences(l, lind + 1, r, rind, buff, ret);buff.pop_back();}if (rind < r.size()) {buff.push_back(r[rind]);mergeSequences(l, lind, r, rind + 1, buff, ret);buff.pop_back();}return;}vector<vector<int>> BSTSequences(TreeNode* root) {vector<vector<int>> ret;if (root == nullptr) {ret.push_back(vector<int> ());return ret;}//找到左右子树的合法序列vector<vector<int>> l_arr = BSTSequences(root->left);vector<vector<int>> r_arr = BSTSequences(root->right);for (auto l : l_arr) {for (auto r : r_arr) {vector<int> buff;buff.push_back(root->val);//合并左右子树的分别序列//参数含义见函数定义部分mergeSequences(l, 0, r, 0, buff, ret);}}return ret;}
};

总结:首先把握好一条原则:根节点必须出现左所以子节点的前面。然后取左子树和右子树的合法序列用递归,合并左右子树合法序列仍然用递归回溯来实现。

Leetcode刷题之二叉搜索树、平衡二叉搜索树3相关推荐

  1. leetcode刷题笔记——剑指offer(二)[回溯、排序、位运算、数学、字符串]

    这里写目录标题 搜索与回溯 剑指 Offer 12. 矩阵中的路径 剑指 Offer 13. 机器人的运动范围 剑指 Offer 34. 二叉树中和为某一值的路径 剑指 Offer 36. 二叉搜索树 ...

  2. C#LeetCode刷题-二叉搜索树

    二叉搜索树篇 # 题名 刷题 通过率 难度 220 存在重复元素 III 19.3% 中等 315 计算右侧小于当前元素的个数 31.9% 困难 327 区间和的个数 29.5% 困难 352 将数据 ...

  3. ​LeetCode刷题实战450:删除二叉搜索树中的节点

    算法的重要性,我就不多说了吧,想去大厂,就必须要经过基础知识和业务逻辑面试+算法面试.所以,为了提高大家的算法能力,这个公众号后续每天带大家做一道算法题,题目就从LeetCode上面选 ! 今天和大家 ...

  4. SQL leetcode 刷题答案(二)

    承接上篇 SQL leetcode 刷题答案https://blog.csdn.net/hahaha66888/article/details/89925981 5.Big Countries sel ...

  5. Leetcode刷题日记(十二)

    又是老台词:欢迎大家来到一晚一度的leetcode刷题日记时间.今天我们来讲讲队列的问题,队列这方面的基础知识需要的同学到博主前面的文章找吧.队列这方面的问题平时博主也是接触得比较少的.下面是一道利用 ...

  6. C#LeetCode刷题-程序员面试金典

    本文由 比特飞 原创发布,欢迎大家踊跃转载. 转载请注明本文地址:C#LeetCode刷题-程序员面试金典 | .Net中文网. C#LEETCODE刷题概述 概述 所有LeetCode程序员面试金典 ...

  7. 一个算法笨蛋的12月leetCode刷题日记

    类似文章 一个算法笨蛋的2021年11月leetCode刷题日记 一个算法笨蛋的2021年12月leetCode刷题日记 一个算法笨蛋的2022年1月leetCode刷题日记 一个算法笨蛋的2022年 ...

  8. leetcode刷题记录总结-7.二叉树

    文章目录 零.二叉树理论 二叉树的种类 满二叉树 完全二叉树 二叉搜索树 平衡二叉搜索树 二叉树的存储方式 二叉树的遍历方式 二叉树的定义 总结 一.二叉树的遍历 [144. 二叉树的前序遍历 ](h ...

  9. 个人LeetCode刷题记录(带题目链接及解答)持续更新

    Leetcode 刷题 注:~[完成]代表还有一些方法没看,最后再看 一.一些需要重刷的典型题: 1.快速排序,归并排序,堆排序(递归的思想) 2.链表中的回文链表,其中的快慢指针,多看,多练 3.链 ...

  10. C#LeetCode刷题-剑指Offer

    本文由 比特飞 原创发布,欢迎大家踊跃转载. 转载请注明本文地址:C#LeetCode刷题-剑指Offer | .Net中文网. C#LEETCODE刷题概述 概述 所有LeetCode剑指Offer ...

最新文章

  1. 使用WakeLock将Android应用程序保持后台唤醒
  2. Java基础-数据类型int,short,char,long,float,double,boolean,byte
  3. ajax和jsonp没有半点关系,跨域问题
  4. Three.js中使用requestAnimationFrame方法实现立方体转动和小球跳动的动画
  5. [WEKA]如何将英文文本数据集转换为ARFF格式
  6. VTK:图片之ImageGradientMagnitude
  7. 这种个性化可视化图也太可爱了吧!
  8. gdb来调式多线程(转)
  9. 11- 深度学习之神经网络核心原理与算法-卷积核典型的CNN网络
  10. 图像分类_04神经网络最优化过程:反向传播+代码实现
  11. 20年,只有谷歌曾经不同
  12. python 安卓模拟器电脑版_【夜神安卓模拟器电脑版】夜神安卓模拟器电脑版 V6.6.0.6免费版官方免费下载_正式版下载-多特软件站...
  13. chinaunix-索引资料
  14. java判断名字是否为张三_用java代码写一个判断名字是不是以K或T开头的?
  15. mybatis-spring
  16. 学习mescroll的下拉刷新、上拉加载
  17. Java 案例三 随机点名器
  18. C语言格式化输出函数printf详解——C语言基础知识
  19. 通过身份证号码判断男女
  20. 我的河海大学计算机考研专业课总结

热门文章

  1. 互联网乡镇综治云平台解决方案
  2. 联想win7无法连接无线网络连接服务器,联想笔记本连不上wifi该怎么处理
  3. 前端进行身份证验证(详细)
  4. 坯子库怎么导入插件_坯子库插件下载|
  5. linux带page参数报错,Linux配置hugepage
  6. DM8达梦数据库学习总结(上)
  7. Pytorch grid_sample解析
  8. python random.sample
  9. kubernetes pod 挂载 ceph rbd
  10. java der 解码_JAVA解析各种编码密钥对(DER、PEM、openssh公钥)