代码随想录算法训练营第二十天|Leetcode530 二叉搜索树的最小绝对差、Leetcode501 二叉搜索树中的众数、Leetcode236 二叉树的最近公共祖先

代码随想录算法训练营第二十天|Leetcode530 二叉搜索树的最小绝对差、Leetcode501 二叉搜索树中的众数、Leetcode236 二叉树的最近公共祖先

● Leetcode530 二叉搜索树的最小绝对差

题目链接:Leetcode530 二叉搜索树的最小绝对差
视频讲解:代码随想录|二叉搜索树的最小绝对差
题目描述:给你一个二叉搜索树的根节点 root ,返回 树中任意两不同节点值之间的最小差值 。
差值是一个正数,其数值等于两值之差的绝对值。

示例 1:
示例一
输入:root = [4,2,6,1,3]
输出:1
示例 2:
示例二
输入:root = [1,0,48,null,null,12,49]
输出:1

提示:
树中节点的数目范围是 [2, 104]
0 <= Node.val <= 105

● 解题思路

方法一:暴力求解
因为二叉排序树中序遍历结果按照递增排列,我们可以先将二叉排序树中序遍历结果存储在数组中,再遍历数组的过程中比较取得最小差值;

方法二:递归
依照递归三部曲进行:
(1)确定递归函数参数和返回值:
我们定义了全局变量result存储最小差,因此不需要设置任何返回值,传入参数仅传入结点cur即可;
(2)确定递归函数的终止条件:
当遍历结点为空时,我们需要向递归的上一层返回;
(3)确定单层遍历逻辑:
选择中序遍历能最好体现二叉搜索树的特性,也能帮助我们寻找相邻结点的最小差值;

在整个递归的过程,cur会自动地按照递归地顺序遍历,如图中红色路线,因此其前驱结点pre也需要跟随着cur移动,如蓝色路线:
cur
pre

方法三:迭代
迭代法和递归一样,curpre都按照中序顺序一直走,只不过是使用栈模拟递归。

● 代码实现

方法一:暴力求解

/**
 * 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> vec;
    void traversal(TreeNode* root)
    {
        if(!root) return;
        traversal(root->left);
        vec.push_back(root->val);
        traversal(root->right);
    }
    int getMinimumDifference(TreeNode* root) {
        traversal(root);
        int result = INT_MAX;
        for(int i = 0; i < vec.size() - 1; i++)
        {
            if(vec[i + 1] - vec[i] < result)
            {
                result = vec[i + 1] - vec[i];
            }
        }
        return result;
    }
};

方法二:递归

/**
 * 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:
    int result = INT_MAX;
    TreeNode* pre = nullptr; //存储按照中序遍历cur的前一个结点
    void traversal(TreeNode* cur)
    {
        //终止条件
        if(!cur) return;

        traversal(cur->left); //左
        if(pre) result = min(result, cur->val - pre->val); //中
        pre = cur; //移动pre
        traversal(cur->right);
    }
    int getMinimumDifference(TreeNode* root) {
        traversal(root);
        return result;
    }
};

方法三:迭代

/**
 * 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:
    int getMinimumDifference(TreeNode* root) {
        stack<TreeNode*> st;
        TreeNode* cur = root;
        TreeNode* pre = NULL;
        int result = INT_MAX;
        while (cur != NULL || !st.empty()) {
            if (cur != NULL) { // 指针来访问节点,访问到最底层
                st.push(cur); // 将访问的节点放进栈
                cur = cur->left;                // 左
            } else {
                cur = st.top();
                st.pop();
                if (pre != NULL) {              // 中
                    result = min(result, cur->val - pre->val);
                }
                pre = cur;
                cur = cur->right;               // 右
            }
        }
        return result;
    }
};

● Leetcode501 二叉搜索树中的众数

题目链接:Leetcode501 二叉搜索树中的众数
视频讲解:代码随想录|二叉搜索树中的众数
题目描述:给你一个含重复值的二叉搜索树(BST)的根节点 root ,找出并返回 BST 中的所有 众数(即,出现频率最高的元素)。
如果树中有不止一个众数,可以按 任意顺序 返回。
假定 BST 满足如下定义:
结点左子树中所含节点的值 小于等于 当前节点的值
结点右子树中所含节点的值 大于等于 当前节点的值
左子树和右子树都是二叉搜索树

示例 1:
示例一
输入:root = [1,null,2,2]
输出:[2]
示例 2:
输入:root = [0]
输出:[0]

提示:
树中节点的数目在范围 [1, 104] 内
-105 <= Node.val <= 105

● 解题思路

因为是二叉排序树,遍历规则我们依然选择中序遍历

整体思路:
我们分别使用cout、maxCount和pre记录在遍历过程中当前元素出现的次数、当前遍历过元素众数出现的最大值和前驱结点(前驱结点帮助我们判断前一个元素和当前元素是否相等,对count进行相应操作);

我们会遇到以下情况:
(1)pre为空时,遍历元素第一次出现,count置为1;
(2)pre->val == cur->val,当前元素出现次数加一;
(3)pre->val == cur->val,当前元素改变,count重新置为1。
在这个过程中我们需要注意:当count == maxCount时,将当前元素插入返回数组中,但会产生疑问:我们在开始maxCount没有进行赋值,记录的也并非是最终最大值,因此我们下一步就是需要对其进行处理;

count > maxCount,即当前元素出现频率大于之前遍历元素最大出现频率,证明返回数组的所有元素不满足条件,我们将其清空并将新的最大频率赋值。
以上操作便是完整的对于二叉排序树众数的处理,同时只遍历一次。

● 代码实现

方法一:递归

/**
 * 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 {
private:
    vector<int> res; //存储返回结果
    int count = 0; //记录当前元素出现个数
    int maxCount = 0; //记录最大元素出现次数
    TreeNode* pre = NULL; //中序遍历的前驱结点
    
    void traversal(TreeNode* cur)
    {
        //终止条件
        if(!cur) return;

        traversal(cur->left); //左
        
        //中
        if(!pre) count = 1;
        else if(pre->val == cur->val) count++;
        else count = 1; //pre->val != cur->val

        pre = cur;//移动pre

        if(count == maxCount)
        {
            res.push_back(cur->val);
        }

        if(count > maxCount)
        {
            maxCount = count;
            res.clear();
            res.push_back(cur->val);
        }
        traversal(cur->right); //右
        return;
    }
public:
    vector<int> findMode(TreeNode* root) {
        count = 0;
        maxCount = 0;
        pre = NULL;
        res.clear();

        traversal(root);
        return res;
    }
};

方法二:迭代

/**
 * 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> findMode(TreeNode* root) {
        int count = 0, maxCount = 0;
        TreeNode* pre = nullptr;
        vector<int> result;
        stack<TreeNode*> st;

        TreeNode* cur = root;
        while(cur || !st.empty())
        {
            if(cur != nullptr)
            {
                st.push(cur);
                cur = cur->left;
            }
            else
            {
                cur = st.top(); st.pop();
                if(pre == nullptr) count = 1;
                else if(pre->val == cur->val) count++;
                else count = 1;

                if(count == maxCount) result.push_back(cur->val);
                if(count > maxCount)
                {
                    result.clear();
                    maxCount = count;
                    result.push_back(cur->val);
                }

                pre = cur;
                cur = cur->right;
            }
        }
        return result;
    }
};

● Leetcode236 二叉树的最近公共祖先

题目链接:Leetcode236 二叉树的最近公共祖先
视频讲解:代码随想录|二叉树的最近公共祖先
题目描述:给定一个二叉树, 找到该树中两个指定节点的最近公共祖先。
百度百科中最近公共祖先的定义为:“对于有根树 T 的两个节点 p、q,最近公共祖先表示为一个节点 x,满足 x 是 p、q 的祖先且 x 的深度尽可能大(一个节点也可以是它自己的祖先)。”

示例 1:
示例一
输入:root = [3,5,1,6,2,0,8,null,null,7,4], p = 5, q = 1
输出:3
解释:节点 5 和节点 1 的最近公共祖先是节点 3 。
示例 2:
示例二
输入:root = [3,5,1,6,2,0,8,null,null,7,4], p = 5, q = 4
输出:5
解释:节点 5 和节点 4 的最近公共祖先是节点 5 。因为根据定义最近公共祖先节点可以为节点本身。
示例 3:
输入:root = [1,2], p = 1, q = 2
输出:1

提示:
·树中节点数目在范围 [2, 105] 内。
·-109 <= Node.val <= 109
·所有 Node.val 互不相同 。
·p != q
·p 和 q 均存在于给定的二叉树中。

● 解题思路

对于本题,我们需要先判断左右子树中是否有结点满足left/right == p/q,然后将结果返回到中结点,对于先左右后中的遍历方式,本题选择后序遍历

对于本题的终止条件:当传入结点为空或者传入结点就是我们的p/q,直接返回结点;

本题符合返回条件的情况为:
当左右结点不为空,即p和q一定在左右子树中,返回当前结点;
但或许会有疑问:万一在左右子树中都有p或者都有q,也会返回当前结点,这就需要注意在本题的提示中:

所有 Node.val 互不相同

而在对中结点的处理中,我们需要判断:
(1)当左结点返回值为空,右结点返回值不为空,我们将右结点返回值向上一次递归返回即可;
(2)当左结点返回值不为空,右结点返回值为空,我们将左结点返回值向上一次递归返回即可;
(3)否则左右结点均没有q & p,返回空即可。

● 代码实现

递归

/**
 * 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* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
        //终止条件
        if(!root) return nullptr;
        if(p == root || q == root) return root;

        TreeNode* left = lowestCommonAncestor(root->left, p, q); //左
        TreeNode* right = lowestCommonAncestor(root->right, p, q); //右

        //中
        if(left && right) return root;
        if(!left && right) return right;
        else if(left && !right) return left;
        else return nullptr;
    }
};
  • 26
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值