代码随想录算法训练营:17/60

非科班学习算法day17 | LeetCode654:最大二叉树 ,Leetcode617:合并二叉树 ,Leetcode700:二叉搜索树中的搜索,Leetcode98:验证二叉搜索树

目录

介绍

一、基础概念补充:

1.二叉搜索树

二、LeetCode题目

1.LeetCode654:最大二叉树

题目解析

2.Leetcode617:合并二叉树

题目解析

3.Leetcode700:二叉搜索树中的搜索

题目解析

4.Leetcode98:验证二叉搜索树

题目解析

总结


介绍

包含LC的两道题目,还有相应概念的补充。

相关图解和更多版本:

代码随想录 (programmercarl.com)https://programmercarl.com/#%E6%9C%AC%E7%AB%99%E8%83%8C%E6%99%AF


一、基础概念补充:

1.二叉搜索树

二叉搜索树(Binary Search Tree,简称BST),又称有序二叉树或排序二叉树,是一种特殊的二叉树数据结构,它具有以下基本性质:

  1. 节点值的有序性

    • 节点的左子树中所有节点的值都小于该节点的值。
    • 节点的右子树中所有节点的值都大于该节点的值。
  2. 唯一性

    • 二叉搜索树中不允许有重复的节点值,即每个值都是唯一的。
  3. 递归定义

    • 整个树要么为空,要么满足以上两个性质,并且左子树和右子树也必须分别是二叉搜索树。

由于这些性质,二叉搜索树支持一些非常高效的算法,例如:

  • 查找:从根节点开始,比较当前节点的值与目标值,根据大小关系决定向左子树还是右子树递归查找,时间复杂度在最佳情况下为O(log n),最坏情况下为O(n)(当树退化为链表时)。

  • 插入:类似于查找,找到合适的位置后创建新节点插入,同时保持树的性质不变。

  • 删除:删除操作相对复杂,需要考虑被删除节点是否有子节点,以及如何调整树结构以保持二叉搜索树的性质,可能包括寻找前驱或后继节点来替代被删除节点。

相关性质还包括:

  • 中序遍历二叉搜索树的中序遍历(左根右的顺序)会得到一个递增的序列,这是其有序性的直接体现,也是验证一个二叉树是否为二叉搜索树的一个简单方法。

  • 高度:二叉搜索树的高度与其节点插入的顺序有关,最坏情况下(每次插入的节点都是当前树的最大或最小值,导致树变为链式结构)高度为n,最好情况下接近log(n)。

为了优化性能,特别是减少高度,发展出了多种平衡二叉搜索树,如AVL树、红黑树等,它们通过额外的平衡条件和旋转操作来保证树的高度大致保持在对数级别,从而确保各种操作的平均时间复杂度为O(log n)。

二、LeetCode题目

1.LeetCode654:最大二叉树

题目链接:654. 最大二叉树 - 力扣(LeetCode)

题目解析

可以发现这道题和106. 从中序与后序遍历序列构造二叉树 - 力扣(LeetCode)非常相似,都是在所给的数组上做文章来构建树,那么具体如何操作呢。

首先需要一个函数来寻找最大值

然后根据信息创建节点

之后根据最大值位置分割数组

最后将得到的数组进行下一层递归

所以中止条件也应该是数组无法分割。

c++代码如下:

/**
 * 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* constructMaximumBinaryTree(vector<int>& nums) {
        if (nums.size() == 1)
            return new TreeNode(nums[0]);
        // 寻找最大值 -----重置初始化!!!------
        int max_val = 0;
        int max_pos = 0;
        for (int i = 0; i < nums.size(); ++i) {
            if (nums[i] > max_val) {
                max_pos = i;
                max_val = nums[i];
            }
        }

        // 创建节点
        TreeNode* root = new TreeNode(max_val);

        // 分割数组
        // vector<int> left_nums = vector<int>(nums.begin(),
        // nums.begin()+max_pos); vector<int> right_nums =
        // vector<int>(nums.begin()+max_pos+1, nums.end());

        // 递归
        if (max_pos > 0) {
            vector<int> left_nums =
                vector<int>(nums.begin(), nums.begin() + max_pos);
            root->left = constructMaximumBinaryTree(left_nums);
        }
        if (max_pos < nums.size() - 1) {
            vector<int> right_nums =
                vector<int>(nums.begin() + max_pos + 1, nums.end());
            root->right = constructMaximumBinaryTree(right_nums);
        }
        return root;
    }
};

注意点1:这里把寻找最大值函数放到里面了,尽量减少函数调用,同时还有个问题就是经过修改研究,尽量减少函数调用可以避免由于传入参量和调用函数之间发生冲突甚至未定义重定义等现象。

注意点2:注意在寻找最大值之前要把参数重新初始化,否则这种写法就会报错。

注意点3:关于中止条件,根据题目条件数组长度是一定大于等于1的,而且调试的时候发现用空来做,自己出了错,所以直接采用等于1时候,直接返回自身。

2.Leetcode617:合并二叉树

题目链接:617. 合并二叉树 - 力扣(LeetCode)

题目解析

一开始考虑的特别复杂,其实根据题目描述,我们不需要完全新建一棵树,而且那样的空间消耗明显更多。所以进行分类讨论:

左节点空,右节点不空,返回右节点

右节点空,左节点不空,返回左节点

左右节点都为空,返回空

左右节点不为空,做加和操作,新建节点

递归下一层的两个树对应位置

c++代码如下:

/**
 * 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(TreeNode* root1, TreeNode* root2) {
        // 左空右回
        if (root1 && !root2)
            return root1;
        // 右空左回
        if (!root1 && root2)
            return root2;
        // 空返回
        if (!root1 && !root2)
            return nullptr;

        // 非空做加和
        // 中
        int sum_num = root1->val + root2->val;
        // 创建新节点
        TreeNode* new_root = new TreeNode(sum_num);
        // 左
        new_root->left = dfs(root1->left, root2->left);
        // 右
        new_root->right = dfs(root1->right, root2->right);

        // 返回二叉树的新根节点
        return new_root;
    }
    TreeNode* mergeTrees(TreeNode* root1, TreeNode* root2) {
        return dfs(root1, root2);
    }
};

3.Leetcode700:二叉搜索树中的搜索

题目链接:700. 二叉搜索树中的搜索 - 力扣(LeetCode)

题目解析

二叉搜索树由于特性的存在,在搜索的时候,不用整棵树都搜索,每次判断之后,只需要选择一边递归。

C++代码如下:

/**
 * 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* searchBST(TreeNode* root, int val) {
        if (!root)
            return nullptr;
        // 需要明确,写递归的时候是有条件的,不需要全部都搜索
        if (root->val == val)
            return root;

        if (root->val > val && root->left)
            return searchBST(root->left, val);
        if (root->val < val && root->right)
            return searchBST(root->right, val);
        return nullptr;
    }
};

注意点1:最后的返回值是null,而且递归函数如果有返回类型一定要用东西接住,要么return,要么赋值给其他变量。

4.Leetcode98:验证二叉搜索树

题目链接:98. 验证二叉搜索树 - 力扣(LeetCode)

题目解析

最直接的做法就是根据二叉搜索树的性质进行中序遍历,然后检验结果是否是一个递增数组。

优化思路就是,借助一个新的自定义指针来记录每一次在遍历过程中的节点,作为下一次递归的前一个结点,这样我们再次中序遍历,就可以直接比较前后两个节点的结果。最后返回值是左右两边共同检查的交。

C++代码如下:

/**
 * 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:
    vector<int> res;
    vector<int> dis(TreeNode* root) {
        if (!root)
            return res;
        dis(root->left);
        res.push_back(root->val);
        dis(root->right);
        return res;
    }

    bool isValidBST(TreeNode* root) {
        dis(root);

        for (int i = 0; i < res.size() - 1; ++i) {
            if (res[i] >= res[i + 1])
                return false;
        }
        return true;
    }
};

记录指针中序遍历c++代码:

/**
 * 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* pre = nullptr;
    bool isValidBST(TreeNode* root) {
        // 现在要做的就是检索,然后
        if (!root)
            return true;

        // 还是中序遍历
        bool left = isValidBST(root->left);
        if (pre && pre->val >= root->val) //先写赋值,转头再想
            return false;
        pre = root;
        bool right = isValidBST(root->right);
        return left && right;
    }
};

总结

打卡第17天,坚持!!!

相关推荐
A懿轩A12 分钟前
C/C++ 数据结构与算法【数组】 数组详细解析【日常学习,考研必备】带图+详细代码
c语言·数据结构·c++·学习·考研·算法·数组
古希腊掌管学习的神13 分钟前
[搜广推]王树森推荐系统——矩阵补充&最近邻查找
python·算法·机器学习·矩阵
云边有个稻草人16 分钟前
【优选算法】—复写零(双指针算法)
笔记·算法·双指针算法
机器视觉知识推荐、就业指导17 分钟前
C++设计模式:享元模式 (附文字处理系统中的字符对象案例)
c++
半盏茶香17 分钟前
在21世纪的我用C语言探寻世界本质 ——编译和链接(编译环境和运行环境)
c语言·开发语言·c++·算法
忘梓.1 小时前
解锁动态规划的奥秘:从零到精通的创新思维解析(3)
算法·动态规划
️南城丶北离1 小时前
[数据结构]图——C++描述
数据结构··最小生成树·最短路径·aov网络·aoe网络
Ronin3051 小时前
11.vector的介绍及模拟实现
开发语言·c++
✿ ༺ ོIT技术༻1 小时前
C++11:新特性&右值引用&移动语义
linux·数据结构·c++
字节高级特工1 小时前
【C++】深入剖析默认成员函数3:拷贝构造函数
c语言·c++