LeetCode刷题复盘笔记:打爆二叉树(续五)(搜索树,二叉树最近公共祖先)

在此非常感谢“代码随想录”的通俗易懂的总结!

(致敬叶师傅和李小龙)

“我不害怕曾经练过一万种踢法的人,但我害怕一种踢法练过一万次的人”(by 叶师傅的徒弟Bruce Lee)
在这里插入图片描述

今天继续打5道,加油,冲冲冲!

具体题目

题目1:700. 二叉搜索树中的搜索

题目描述:
给定二叉搜索树(BST)的根节点和一个值。 你需要在BST中找到节点值等于给定值的节点。 返回以该节点为根的子树。 如果节点不存在,则返回 NULL。
LeetCode刷题复盘笔记:打爆二叉树(续五)(搜索树,二叉树最近公共祖先)_第1张图片

C++代码

二叉搜索树的遍历方式,因为二叉搜索树的有序性,遍历的时候要比普通二叉树简单很多。 所以针对二叉搜索树的题目,一定要利用其特性。

方法一、递归方法

class Solution {
public:
    TreeNode* searchBST(TreeNode* root, int val) {
        if(root==NULL || root->val==val) return root;
        else if(root->val>val) return searchBST(root->left,val);
        else if(root->val<val) return searchBST(root->right,val);
        return NULL;
    }
};

方法二、迭代方法

class Solution {
public:
    TreeNode* searchBST(TreeNode* root, int val) {
        while(root){
            if(root->val>val) root=root->left;
            else if(root->val<val) root=root->right;
            else return root;
        }
        return NULL;
    }
};

题目2:98. 验证二叉搜索树

题目描述:
给定一个二叉树,判断其是否是一个有效的二叉搜索树。

假设一个二叉搜索树具有如下特征:

节点的左子树只包含小于当前节点的数。
节点的右子树只包含大于当前节点的数。
所有左子树和右子树自身必须也是二叉搜索树。
LeetCode刷题复盘笔记:打爆二叉树(续五)(搜索树,二叉树最近公共祖先)_第2张图片

C++代码:

在中序遍历下,输出的二叉搜索树节点的数值是有序序列。
有了这个特性,验证二叉搜索树,只要比较一下,这个数组是否是有序的就可以了

class Solution {
public:
    vector<int> vec;
    void traversal(TreeNode* root){
        if(root==NULL) return;
        traversal(root->left);
        vec.push_back(root->val);
        traversal(root->right);
    }
    bool isValidBST(TreeNode* root) {   
        traversal(root);
        for(int i=1;i<vec.size();++i){
            if(vec[i-1]>=vec[i]) return false;
        }
        return true;
    }
};

题目3:530. 二叉搜索树的最小绝对差

题目描述:
给你一棵所有节点为非负值的二叉搜索树,请你计算树中任意两节点的差的绝对值的最小值。
LeetCode刷题复盘笔记:打爆二叉树(续五)(搜索树,二叉树最近公共祖先)_第3张图片

C++代码:

二叉搜索树采用中序遍历,其实就是一个有序数组。最小差值一定在相邻两个节点中间产生

方法一、递归方法(双指针)

class Solution {
private:
int result=INT_MAX;
TreeNode* pre;
    void traversal(TreeNode* root){
        if(root==NULL) return;
        traversal(root->left);
        if(pre){
            result=min(result,root->val-pre->val);
        }
        pre=root;
        traversal(root->right);
    }
public:
    int getMinimumDifference(TreeNode* root) {
        traversal(root);
        return result;        
    }
};

方法二、迭代方法

class Solution {
public:
    int getMinimumDifference(TreeNode* root) {
        if(root==NULL) return 0;
        stack<TreeNode*> st;
        int result=INT_MAX;
        TreeNode* pre=NULL;
        TreeNode* cur=root;
        while(!st.empty() || cur){
            if(cur){
                st.push(cur);
                cur=cur->left;
            }
            else{
                cur=st.top();
                st.pop();
                if(pre){
                    result=min(result,cur->val-pre->val);
                }
                pre=cur;
                cur=cur->right;
            }
        }
        return result;     
    }
};

题目4:501.二叉搜索树中的众数

题目描述:
给定一个有相同值的二叉搜索树(BST),找出 BST 中的所有众数(出现频率最高的元素)。

假定 BST 有如下定义:

结点左子树中所含结点的值小于等于当前结点的值
结点右子树中所含结点的值大于等于当前结点的值
左子树和右子树都是二叉搜索树
例如:

给定 BST [1,null,2,2],
LeetCode刷题复盘笔记:打爆二叉树(续五)(搜索树,二叉树最近公共祖先)_第4张图片

返回[2].

提示:如果众数超过1个,不需考虑输出顺序

进阶:你可以不使用额外的空间吗?(假设由递归产生的隐式调用栈的开销不被计算在内)

C++代码:

思路核心:
如果 频率count 等于 maxCount(最大频率),当然要把这个元素加入到结果集中(以下代码为result数组)
当频率count 大于 maxCount的时候,不仅要更新maxCount,而且要清空结果集(以下代码为result数组),因为结果集之前的元素都失效了
方法一、递归法(中序遍历)

class Solution {
private:
int count;
int maxcount;
TreeNode* pre;
vector<int> result;
    void traversal(TreeNode* cur){
        if(cur==NULL) return;
        traversal(cur->left);
        if(pre==NULL) count=1;
        else if(pre->val==cur->val) count++;
        else count=1;
        // pre=cur;
        if(count==maxcount) result.push_back(cur->val);
        if(count>maxcount){
            result.clear();
            result.push_back(cur->val);
            maxcount=count;
        }
        pre=cur;
        traversal(cur->right);
        return;
    }    
public:
    vector<int> findMode(TreeNode* root) {
        count=0;
        maxcount=0;
        pre=NULL;
        traversal(root);
        return result;
    }
};

方法二、迭代法(中序遍历)

class Solution {
public:
    vector<int> findMode(TreeNode* root) {
        vector<int> result;
        if(root==NULL) return result;
        stack<TreeNode*> st;
        int count=0;
        int maxcount=0;
        TreeNode* pre=NULL;
        TreeNode* cur=root;
        while(!st.empty() || cur){
            if(cur){
                st.push(cur);
                cur=cur->left;
            }
            else{
                cur=st.top();
                st.pop();
                if(pre==NULL) 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();
                    result.push_back(cur->val);
                    maxcount=count;
                }
                pre=cur;
                cur=cur->right;
            }
        }
        return result;
    }
};

题目5:236. 二叉树的最近公共祖先

题目描述:
给定一个二叉树, 找到该树中两个指定节点的最近公共祖先。

百度百科中最近公共祖先的定义为:“对于有根树 T 的两个结点 p、q,最近公共祖先表示为一个结点 x,满足 x 是 p、q 的祖先且 x 的深度尽可能大(一个节点也可以是它自己的祖先)。”

示例 1:
LeetCode刷题复盘笔记:打爆二叉树(续五)(搜索树,二叉树最近公共祖先)_第5张图片

输入: root = [3,5,1,6,2,0,8,null,null,7,4], p = 5, q = 1
输出: 3
解释: 节点 5 和节点 1 的最近公共祖先是节点 3。

示例 2:
LeetCode刷题复盘笔记:打爆二叉树(续五)(搜索树,二叉树最近公共祖先)_第6张图片

输入: 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

说明:
所有节点的值都是唯一的
p、q 为不同节点且均存在于给定的二叉树中

C++代码:

遇到这个题目首先想的是要是能自底向上查找就好了,这样就可以找到公共祖先了。

那么二叉树如何可以自底向上查找呢?

回溯啊,二叉树回溯的过程就是从低到上。

后序遍历就是天然的回溯过程,最先处理的一定是叶子节点。

接下来就看如何判断一个节点是节点q和节点p的公共公共祖先呢。

「如果找到一个节点,发现左子树出现结点p,右子树出现节点q,或者 左子树出现结点q,右子树出现节点p,那么该节点就是节点p和q的最近公共祖先。」

使用后序遍历,回溯的过程,就是从低向上遍历节点,一旦发现如何这个条件的节点,就是最近公共节点了。

class Solution {
public:
    TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
        if(root==p || root==q || root==NULL) return root;
        TreeNode* lefttree=lowestCommonAncestor(root->left,p,q);
        TreeNode* righttree=lowestCommonAncestor(root->right,p,q);
        if(lefttree && righttree) return root;
        else if(lefttree && righttree==NULL) return lefttree;
        else if(lefttree==NULL && righttree) return righttree;
        else return NULL;
    }
};

1.求最小公共祖先,需要从底向上遍历,那么二叉树,只能通过后序遍历(即:回溯)实现从低向上的遍历方式。

2.在回溯的过程中,必然要遍历整颗二叉树,即使已经找到结果了,依然要把其他节点遍历完,因为要使用递归函数的返回值(也就是代码中的left和right)做逻辑判断。

3.要理解如果返回值left为空,right不为空为什么要返回right,为什么可以用返回right传给上一层结果。


欢迎大家扫码关注本人公众号:编程复盘与思考随笔

(关注后可以免费获得本人在csdn发布的资源源码)

公众号主要记录编程和刷题时的总结复盘笔记和心得!并且分享读书、工作、生活中的一些思考感悟!
在这里插入图片描述

想要组队一起参加阿里天池,kaggle,百度飞浆,科大讯飞等AI相关的比赛的同学可以扫下面的二维码加微信一起讨论学习!

在这里插入图片描述

你可能感兴趣的:(数据结构与算法,二叉树,c++,数据结构,算法,leetcode)