首页 > 编程语言 >代码随想录算法训练营第十九天 | 654.最大二叉树,617.合并二叉树,700.二叉搜索树中的搜索,98.验证二叉搜索树

代码随想录算法训练营第十九天 | 654.最大二叉树,617.合并二叉树,700.二叉搜索树中的搜索,98.验证二叉搜索树

时间:2023-02-05 16:23:14浏览次数:65  
标签:right TreeNode val int 二叉 搜索 二叉树 root left

一、参考资料

最大二叉树

题目链接/文章讲解:https://programmercarl.com/0654.%E6%9C%80%E5%A4%A7%E4%BA%8C%E5%8F%89%E6%A0%91.html

视频讲解:https://www.bilibili.com/video/BV1MG411G7ox

合并二叉树

题目链接/文章讲解:https://programmercarl.com/0617.%E5%90%88%E5%B9%B6%E4%BA%8C%E5%8F%89%E6%A0%91.html

视频讲解:https://www.bilibili.com/video/BV1m14y1Y7JK

二叉搜索树中的搜索

题目链接/文章讲解: https://programmercarl.com/0700.%E4%BA%8C%E5%8F%89%E6%90%9C%E7%B4%A2%E6%A0%91%E4%B8%AD%E7%9A%84%E6%90%9C%E7%B4%A2.html

视频讲解:https://www.bilibili.com/video/BV1wG411g7sF

验证二叉搜索树

题目链接/文章讲解:https://programmercarl.com/0098.%E9%AA%8C%E8%AF%81%E4%BA%8C%E5%8F%89%E6%90%9C%E7%B4%A2%E6%A0%91.html

视频讲解:https://www.bilibili.com/video/BV18P411n7Q4

二、LeetCode654.最大二叉树

https://leetcode.cn/problems/maximum-binary-tree/description/

示例一:

示例二:

给定一个不重复的整数数组 nums 。 最大二叉树 可以用下面的算法从 nums 递归地构建:
创建一个根节点,其值为 nums 中的最大值。
递归地在最大值 左边 的 子数组前缀上 构建左子树。
递归地在最大值 右边 的 子数组后缀上 构建右子树。
返回 nums 构建的 最大二叉树 。

示例 1:
输入:nums = [3,2,1,6,0,5] 输出:[6,3,5,null,2,0,null,null,1] 解释:递归调用如下所示: - [3,2,1,6,0,5] 中的最大值是 6 ,左边部分是 [3,2,1] ,右边部分是 [0,5] 。 - [3,2,1] 中的最大值是 3 ,左边部分是 [] ,右边部分是 [2,1] 。 - 空数组,无子节点。 - [2,1] 中的最大值是 2 ,左边部分是 [] ,右边部分是 [1] 。 - 空数组,无子节点。 - 只有一个元素,所以子节点是一个值为 1 的节点。 - [0,5] 中的最大值是 5 ,左边部分是 [0] ,右边部分是 [] 。 - 只有一个元素,所以子节点是一个值为 0 的节点。 - 空数组,无子节点。
示例 2:
输入:nums = [3,2,1] 输出:[3,null,2,null,1]
提示:
1 <= nums.length <= 1000
0 <= nums[i] <= 1000
nums 中的所有整数 互不相同
  1. /**
  2. * Definition for a binary tree node.
  3. * struct TreeNode {
  4. * int val;
  5. * TreeNode *left;
  6. * TreeNode *right;
  7. * TreeNode() : val(0), left(nullptr), right(nullptr) {}
  8. * TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
  9. * TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
  10. * };
  11. */
  12. class Solution {
  13. private:
  14. TreeNode* constructMaxTree(vector<int>& nums, int leftBegin, int rightEnd) {
  15. int maxVal = nums[leftBegin];
  16. int index = leftBegin;
  17. if (leftBegin >= rightEnd) return nullptr;
  18. // if (nums.size() == 1) return new TreeNode(nums[0]);
  19. for (int i = leftBegin; i < rightEnd; i++) {
  20. if (nums[i] > maxVal) {
  21. maxVal = nums[i];
  22. index = i;
  23. }
  24. }
  25. cout << maxVal << " " << index << endl;
  26. TreeNode* root = new TreeNode(maxVal);
  27. // 左
  28. if (index > 0) {
  29. root->left = constructMaxTree(nums, leftBegin, index);
  30. }
  31. // 右
  32. if (index < rightEnd- 1) {
  33. root->right = constructMaxTree(nums, index + 1, rightEnd);
  34. }
  35. return root;
  36. }
  37. public:
  38. TreeNode* constructMaximumBinaryTree(vector<int>& nums) {
  39. if (nums.size() == 1) {
  40. return new TreeNode(nums[0]);
  41. }
  42. return constructMaxTree(nums, 0, nums.size()); // 左闭右开
  43. }
  44. };

注意事项记录:

  1. class Solution {
  2. // 几点需要注意的:
  3. // 1. 所有区间边界问题,左闭右开
  4. // 2. 递归结束的条件:递归结束的条件是区间左端点的值≥区间右端点的值
  5. // 3. 在采用这种传下标的方法时,maxVal和index的初始化要用下标来表示,而不是默认0
  6. // 4. 加if判断是为了不让空节点进入递归
  7. private:
  8. TreeNode* constructMaxTree(vector<int>& nums, int leftBegin, int rightEnd) {
  9. int maxVal = nums[leftBegin];
  10. int index = leftBegin;
  11. if (leftBegin >= rightEnd) return NULL;
  12. for (int i = leftBegin; i < rightEnd; i++) {
  13. if (nums[i] > maxVal) {
  14. maxVal = nums[i];
  15. index = i;
  16. }
  17. }
  18. // cout << maxVal << " " << index << endl;
  19. TreeNode* root = new TreeNode(maxVal);
  20. // 左 // 这里加了判断是为了不让空节点进入递归
  21. if (index > 0) {
  22. root->left = constructMaxTree(nums, leftBegin, index);
  23. }
  24. // 右 // 这里加了判断是为了不让空节点进入递归
  25. if (index < rightEnd- 1) {
  26. root->right = constructMaxTree(nums, index + 1, rightEnd);
  27. }
  28. return root;
  29. }
  30. public:
  31. TreeNode* constructMaximumBinaryTree(vector<int>& nums) {
  32. if (nums.size() == 1) {
  33. return new TreeNode(nums[0]);
  34. }
  35. return constructMaxTree(nums, 0, nums.size()); // 左闭右开
  36. }
  37. };

三、LeetCode617.合并二叉树

https://leetcode.cn/problems/merge-two-binary-trees/description/

示例1:

给你两棵二叉树: root1 和 root2 。
想象一下,当你将其中一棵覆盖到另一棵之上时,两棵树上的一些节点将会重叠(而另一些不会)。你需要将这两棵树合并成一棵新二叉树。合并的规则是:如果两个节点重叠,那么将这两个节点的值相加作为合并后节点的新值;否则, 不为 null 的节点将直接作为新二叉树的节点。
返回合并后的二叉树。
注意: 合并过程必须从两个树的根节点开始。

示例 1:
输入:root1 = [1,3,2,5], root2 = [2,1,3,null,4,null,7] 输出:[3,4,5,5,4,null,7]
示例 2:
输入:root1 = [1], root2 = [1,2] 输出:[2,2]
提示:
两棵树中的节点数目在范围 [0, 2000] 内
-10^4 <= Node.val <= 10^4
  1. /**
  2. * Definition for a binary tree node.
  3. * struct TreeNode {
  4. * int val;
  5. * TreeNode *left;
  6. * TreeNode *right;
  7. * TreeNode() : val(0), left(nullptr), right(nullptr) {}
  8. * TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
  9. * TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
  10. * };
  11. */
  12. class Solution {
  13. // 本题旨在考察如何同步处理两个树 —— 采用前中后序遍历均可以!(代码以前序为例)
  14. public:
  15. TreeNode* mergeTrees(TreeNode* root1, TreeNode* root2) {
  16. if (root1 == NULL) return root2; // 如果root1为空,合并之后就应该是root2
  17. if (root2 == NULL) return root1; // 如果root2为空,合并之后就应该是root1
  18. // 修改了root1的数值和结构
  19. root1->val += root2->val; // 中
  20. root1->left = mergeTrees(root1->left, root2->left); // 左
  21. root1->right = mergeTrees(root1->right, root2->right); // 右
  22. return root1;
  23. }
  24. };
  1. /**
  2. * Definition for a binary tree node.
  3. * struct TreeNode {
  4. * int val;
  5. * TreeNode *left;
  6. * TreeNode *right;
  7. * TreeNode() : val(0), left(nullptr), right(nullptr) {}
  8. * TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
  9. * TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
  10. * };
  11. */
  12. class Solution {
  13. // 不改变root1,而是新建一棵树
  14. public:
  15. TreeNode* mergeTrees(TreeNode* root1, TreeNode* root2) {
  16. if (root1 == NULL) return root2; // 如果root1为空,合并之后就应该是root2
  17. if (root2 == NULL) return root1; // 如果root2为空,合并之后就应该是root1
  18. // 新建树
  19. TreeNode* root = new TreeNode(root1->val + root2->val); // 中
  20. root->left = mergeTrees(root1->left, root2->left); // 左
  21. root->right = mergeTrees(root1->right, root2->right); // 右
  22. return root;
  23. }
  24. };

队列迭代法:(不是很熟练——参考代码随想录)

  1. class Solution {
  2. public:
  3. TreeNode* mergeTrees(TreeNode* t1, TreeNode* t2) {
  4. if (t1 == NULL) return t2;
  5. if (t2 == NULL) return t1;
  6. queue<TreeNode*> que;
  7. que.push(t1);
  8. que.push(t2);
  9. while(!que.empty()) {
  10. TreeNode* node1 = que.front(); que.pop();
  11. TreeNode* node2 = que.front(); que.pop();
  12. // 此时两个节点一定不为空,val相加
  13. node1->val += node2->val;
  14. // 如果两棵树左节点都不为空,加入队列
  15. if (node1->left != NULL && node2->left != NULL) {
  16. que.push(node1->left);
  17. que.push(node2->left);
  18. }
  19. // 如果两棵树右节点都不为空,加入队列
  20. if (node1->right != NULL && node2->right != NULL) {
  21. que.push(node1->right);
  22. que.push(node2->right);
  23. }
  24. // 当t1的左节点 为空 t2左节点不为空,就赋值过去
  25. if (node1->left == NULL && node2->left != NULL) {
  26. node1->left = node2->left;
  27. }
  28. // 当t1的右节点 为空 t2右节点不为空,就赋值过去
  29. if (node1->right == NULL && node2->right != NULL) {
  30. node1->right = node2->right;
  31. }
  32. }
  33. return t1;
  34. }
  35. };

四、LeetCode700.二叉搜索树中的搜索

https://leetcode.cn/problems/search-in-a-binary-search-tree/description/

示例1:

示例2:

给定二叉搜索树(BST)的根节点 root 和一个整数值 val。
你需要在 BST 中找到节点值等于 val 的节点。 返回以该节点为根的子树。 如果节点不存在,则返回 null 。

示例 1:
输入:root = [4,2,7,1,3], val = 2 输出:[2,1,3]
示例 2:
输入:root = [4,2,7,1,3], val = 5 输出:[]
提示:
数中节点数在 [1, 5000] 范围内
1 <= Node.val <= 10^7
root 是二叉搜索树
1 <= val <= 10^7
  1. /**
  2. * Definition for a binary tree node.
  3. * struct TreeNode {
  4. * int val;
  5. * TreeNode *left;
  6. * TreeNode *right;
  7. * TreeNode() : val(0), left(nullptr), right(nullptr) {}
  8. * TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
  9. * TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
  10. * };
  11. */
  12. class Solution {
  13. // version1: 递归法
  14. public:
  15. TreeNode* searchBST(TreeNode* root, int val) {
  16. TreeNode* res = NULL;
  17. if (root == NULL || root->val == val) return root;
  18. if (val < root->val) res = searchBST(root->left, val);
  19. if (val > root->val) res = searchBST(root->right, val);
  20. return res;
  21. }
  22. };
  23. /**
  24. * Definition for a binary tree node.
  25. * struct TreeNode {
  26. * int val;
  27. * TreeNode *left;
  28. * TreeNode *right;
  29. * TreeNode() : val(0), left(nullptr), right(nullptr) {}
  30. * TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
  31. * TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
  32. * };
  33. */
  34. class Solution {
  35. // version2: 继续递归(简化版)
  36. public:
  37. TreeNode* searchBST(TreeNode* root, int val) {
  38. if (root == NULL || root->val == val) return root;
  39. if (root->val > val) return searchBST(root->left, val);
  40. if (root->val < val) return searchBST(root->right, val);
  41. return NULL;
  42. }
  43. };
  1. /**
  2. * Definition for a binary tree node.
  3. * struct TreeNode {
  4. * int val;
  5. * TreeNode *left;
  6. * TreeNode *right;
  7. * TreeNode() : val(0), left(nullptr), right(nullptr) {}
  8. * TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
  9. * TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
  10. * };
  11. */
  12. class Solution {
  13. // version3: 迭代法
  14. public:
  15. TreeNode* searchBST(TreeNode* root, int val) {
  16. while (root != NULL) {
  17. if (root->val > val) root = root->left;
  18. else if (root->val < val) root = root->right;
  19. else return root;
  20. }
  21. return NULL;
  22. }
  23. };

因为二叉搜索树的有序性,遍历的时候要比普通二叉树简单很多。

五、LeetCode98.验证二叉搜索树

https://leetcode.cn/problems/validate-binary-search-tree/description/

回顾一下中序遍历的迭代法写法

// https://programmercarl.com/%E4%BA%8C%E5%8F%89%E6%A0%91%E7%9A%84%E8%BF%AD%E4%BB%A3%E9%81%8D%E5%8E%86.html#%E4%B8%AD%E5%BA%8F%E9%81%8D%E5%8E%86-%E8%BF%AD%E4%BB%A3%E6%B3%95

示例1:

示例2:

给你一个二叉树的根节点 root ,判断其是否是一个有效的二叉搜索树。
有效 二叉搜索树定义如下:
节点的左子树只包含 小于 当前节点的数。
节点的右子树只包含 大于 当前节点的数。
所有左子树和右子树自身必须也是二叉搜索树。

示例 1:
输入:root = [2,1,3] 输出:true
示例 2:
输入:root = [5,1,4,null,null,3,6] 输出:false 解释:根节点的值是 5 ,但是右子节点的值是 4 。
提示:
树中节点数目范围在[1, 104] 内
-2^31 <= Node.val <= 2^31 - 1
  1. /**
  2. * Definition for a binary tree node.
  3. * struct TreeNode {
  4. * int val;
  5. * TreeNode *left;
  6. * TreeNode *right;
  7. * TreeNode() : val(0), left(nullptr), right(nullptr) {}
  8. * TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
  9. * TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
  10. * };
  11. */
  12. class Solution {
  13. // version1:直接比较(为了满足了测试样例而定义初始值)
  14. public:
  15. long long maxVal = LONG_MIN; // 注意:这个最小值应该写到递归函数外面,不然每次递归子树时都会重新进入
  16. bool isValidBST(TreeNode* root) {
  17. if (root == NULL) return true;
  18. bool left = isValidBST(root->left); // 左
  19. if (root->val > maxVal) {
  20. maxVal = root->val; // 中
  21. } else {
  22. return false;
  23. }
  24. bool right = isValidBST(root->right); //右
  25. return left && right;
  26. }
  27. };
  1. /**
  2. * Definition for a binary tree node.
  3. * struct TreeNode {
  4. * int val;
  5. * TreeNode *left;
  6. * TreeNode *right;
  7. * TreeNode() : val(0), left(nullptr), right(nullptr) {}
  8. * TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
  9. * TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
  10. * };
  11. */
  12. class Solution {
  13. // version2:双指针优化 —— 需要注意的是 判断前一个节点≥当前节点时,返回false,而不是仅大于
  14. public:
  15. TreeNode* pre = NULL; // pre记录前一个节点的值
  16. bool isValidBST(TreeNode* root) {
  17. if (root == NULL) return true;
  18. bool left = isValidBST(root->left); // 左
  19. if (pre != NULL && pre->val >= root->val) return false;
  20. pre = root;
  21. bool right = isValidBST(root->right); //右
  22. return left && right;
  23. }
  24. };
  1. /**
  2. * Definition for a binary tree node.
  3. * struct TreeNode {
  4. * int val;
  5. * TreeNode *left;
  6. * TreeNode *right;
  7. * TreeNode() : val(0), left(nullptr), right(nullptr) {}
  8. * TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
  9. * TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
  10. * };
  11. */
  12. class Solution {
  13. // version 3: 迭代法模拟二叉树中序遍历
  14. public:
  15. bool isValidBST(TreeNode* root) {
  16. stack<TreeNode*> st;
  17. TreeNode* pre = NULL; // 记录前一个节点
  18. TreeNode* cur = root;
  19. while (cur != NULL || !st.empty()) {
  20. if (cur != NULL) {
  21. st.push(cur);
  22. cur = cur->left; // 左
  23. } else {
  24. cur = st.top(); // 中
  25. st.pop();
  26. if (pre != NULL && pre->val >= cur->val) {
  27. return false;
  28. } else {
  29. pre = cur;
  30. }
  31. cur = cur->right; // 右
  32. }
  33. }
  34. return true;
  35. }
  36. };

我写的版本

  1. /**
  2. * Definition for a binary tree node.
  3. * struct TreeNode {
  4. * int val;
  5. * TreeNode *left;
  6. * TreeNode *right;
  7. * TreeNode() : val(0), left(nullptr), right(nullptr) {}
  8. * TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
  9. * TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
  10. * };
  11. */
  12. class Solution {
  13. // version 4: 我写的迭代法模拟二叉树中序遍历
  14. // https://programmercarl.com/%E4%BA%8C%E5%8F%89%E6%A0%91%E7%9A%84%E8%BF%AD%E4%BB%A3%E9%81%8D%E5%8E%86.html#%E4%B8%AD%E5%BA%8F%E9%81%8D%E5%8E%86-%E8%BF%AD%E4%BB%A3%E6%B3%95
  15. public:
  16. bool isValidBST(TreeNode* root) {
  17. stack<TreeNode*> st;
  18. TreeNode* pre = NULL; // 记录前一个节点
  19. TreeNode* cur = root;
  20. while (cur != NULL || !st.empty()) {
  21. if (cur != NULL) {
  22. st.push(cur);
  23. cur = cur->left;
  24. }
  25. if (cur != NULL) {
  26. st.push(cur);
  27. cur = cur->left; // 左
  28. } else {
  29. cur = st.top(); // 中
  30. st.pop();
  31. if (pre != NULL && pre->val >= cur->val) {
  32. return false;
  33. } else {
  34. pre = cur;
  35. }
  36. cur = cur->right; // 右
  37. }
  38. }
  39. return true;
  40. }
  41. };
  • 陷阱1

不能单纯的比较左节点小于中间节点,右节点大于中间节点就完事了。

  • 陷阱2

样例中最小节点 可能是int的最小值,如果这样使用最小的int来比较也是不行的。

此时可以初始化比较元素为longlong的最小值。

进一步解决陷阱2,引入了version2的代码,通过双指针优化

总结:

做到现在已经能够看完视频独立写出代码了,下一步通过复习希望能熟练掌握解题的思路,还有什么类型的题用前序遍历,什么类型的题用后序遍历,什么类型的题用中序遍历,什么类型的题不用考虑遍历顺序等等。

参考以下这个博主的解答:

https://blog.csdn.net/weixin_44561338/article/details/107946716

  • 前序遍历(中左右)

  • 第一次访问就输出数据

  • 适合静态访问

  • 中序遍历(左中右)

  • 对于二分搜索树,输出结果是有序的

  • 适合顺序输出结果

  • 后序遍历(左右中)

  • 对节点操作时必访问过其子节点

  • 适合进行破坏性操作(删除节点)

刷题加油鸭~~

标签:right,TreeNode,val,int,二叉,搜索,二叉树,root,left
From: https://www.cnblogs.com/ucaszym/p/17093512.html

相关文章