DAY18|二叉树Part06|LeetCode: 530.二叉搜索树的最小绝对差、501. 二叉搜索树中的众数、236.二叉树的最近公共祖先

目录

[LeetCode: 530.二叉搜索树的最小绝对差](#LeetCode: 530.二叉搜索树的最小绝对差)

基本思路

有序数组法

C++代码

双指针法

C++代码

[LeetCode: 501. 二叉搜索树中的众数](#LeetCode: 501. 二叉搜索树中的众数)

哈希法

C++代码

双指针法

C++代码

[LeetCode: 236.二叉树的最近公共祖先](#LeetCode: 236.二叉树的最近公共祖先)

基本思路

C++代码


LeetCode: 530.二叉搜索树的最小绝对差

力扣代码链接

l文字讲解:LeetCode: 530.二叉搜索树的最小绝对差

视频讲解:二叉搜索树中,需要掌握如何双指针遍历!

基本思路

题目中要求在二叉搜索树上任意两节点的差的绝对值的最小值。因为是有序二叉树,将所有二叉树的节点按照中序遍历,实际上可以看做是一个有序数组 。这样任意两个不同节点值之间的最小差值不就一定产生在相邻的两个节点之间。

有序数组法

这个题目存在两种解法。第一种解法是最容易想到的,就是将二叉树转换成有序数组,然后在遍历一遍数组,这样就可以找到最小绝对差了。

C++代码

class Solution {
private:
vector<int> vec;
void traversal(TreeNode* root) {
    if (root == NULL) return;
    traversal(root->left);
    vec.push_back(root->val); // 将二叉搜索树转换为有序数组
    traversal(root->right);
}
public:
    int getMinimumDifference(TreeNode* root) {
        vec.clear();
        traversal(root);
        if (vec.size() < 2) return 0;
        int result = INT_MAX;
        for (int i = 1; i < vec.size(); i++) { // 统计有序数组的最小差值
            result = min(result, vec[i] - vec[i-1]);
        }
        return result;
    }
};

双指针法

另外一种方法就是在对搜索二叉树做中序遍历的时候就可以进行计算,我们可以考虑使用双指针法,这样就可以不构建数组便可以得到结果了,但是我们如何利用双指针求解呢?

  • 定义全局变量

result:用来记录搜索二叉树任意两个节点的最小值,初试值设为INT_MAX。

pre:pre指针需要事先定义好,否则在迭代中不就乱套了?

int result = INT_MAX;
TreeNode* pre = NULL;
  • 确定函数参数和返回值

参数:传入当前遍历的节点指针。

返回值:返回值应该为void,因为已经存在全局变量result对结果进行记录了。

void traversal(TreeNode* cur)
  • 确定终止条件

如果当前节点为空,则停止。

if (cur == NULL) return;
  • 确定单层递归逻辑

按照中序遍历的顺序,这里逻辑很神奇,一定要弄明白,可以试着自己模拟写一下过程。

traversal(cur->lfet); //左
if (pre != NULL)
  result = min(result, cur->val - pre->val); //中
//pre指向上一个节点!
pre = cur;
traversal(cur->right); //右

C++代码

class Solution {
private:
int result = INT_MAX;
TreeNode* pre = NULL;
void traversal(TreeNode* cur) {
    if (cur == NULL) return;
    traversal(cur->left);   // 左
    if (pre != NULL){       // 中
        result = min(result, cur->val - pre->val);
    }
    pre = cur; // 记录前一个
    traversal(cur->right);  // 右
}
public:
    int getMinimumDifference(TreeNode* root) {
        traversal(root);
        return result;
    }
};

LeetCode: 501. 二叉搜索树中的众数

力扣代码链接

文字讲解:LeetCode: 501. 二叉搜索树中的众数

视频讲解:不仅双指针,还有代码技巧可以惊艳到你!

哈希法

这个题目,可以看做是普通二叉树和搜索二叉树来进行求解,对于一颗普通二叉树来讲,如果我们想要统计二叉树中的众数,最容易想到的就是使用哈希表来统计每个数出现的频率,使用map进行统计。

//用前中后序哪种遍历也不重要,因为就是要全遍历一遍,怎么个遍历法都行
// map<int, int> key:元素,value:出现频率
void searchBST(TreeNode* cur, unordered_map<int, int>& map) { // 前序遍历
    if (cur == NULL) return ;
    map[cur->val]++; // 统计元素频率
    searchBST(cur->left, map);
    searchBST(cur->right, map);
    return ;
}

不过需要注意的是,**C++中如果使用std::map或者std::multimap可以对key排序,但不能对value排序。**所以要把map转化数组即vector,再进行排序。

bool static cmp (const pair<int, int>& a, const pair<int, int>& b) {
    return a.second > b.second; // 按照频率从大到小排序
}

vector<pair<int, int>> vec(map.begin(), map.end());
sort(vec.begin(), vec.end(), cmp); // 给频率排个序

前面已经对数组中的元素出现频率进行排序,只需要把出现频率最高的元素取出即可。

result.push_back(vec[0].first);
for (int i = 1; i < vec.size(); i++) {
    // 取最高的放到result数组中
    if (vec[i].second == vec[0].second) result.push_back(vec[i].first);
    else break;
}
return result;

C++代码

class Solution {
private:

void searchBST(TreeNode* cur, unordered_map<int, int>& map) { // 前序遍历
    if (cur == NULL) return ;
    map[cur->val]++; // 统计元素频率
    searchBST(cur->left, map);
    searchBST(cur->right, map);
    return ;
}
bool static cmp (const pair<int, int>& a, const pair<int, int>& b) {
    return a.second > b.second;
}
public:
    vector<int> findMode(TreeNode* root) {
        unordered_map<int, int> map; // key:元素,value:出现频率
        vector<int> result;
        if (root == NULL) return result;
        searchBST(root, map);
        vector<pair<int, int>> vec(map.begin(), map.end());
        sort(vec.begin(), vec.end(), cmp); // 给频率排个序
        result.push_back(vec[0].first);
        for (int i = 1; i < vec.size(); i++) {
            // 取最高的放到result数组中
            if (vec[i].second == vec[0].second) result.push_back(vec[i].first);
            else break;
        }
        return result;
    }
};

双指针法

这个题目依旧可以使用双指针法进行求解,不过前提是这个题目声明是搜索二叉树,并且我们必须使用中序遍历,因为只有遍历二叉树才是有序的。那么我们应该如何求出二叉树中的众数呢?

  • 定义全局变量

pre:记录当前节点的上一个节点

maxCount:用来记录元素出现的最大频率

count:用来记录元素出现的频率

result:用来记录结果

TreeNode* pre = NULL;
int count = 0;
int maxCount = 0;
vector<int> result;
  • 确定函数参数和返回值

参数:传入一个指针,指向当前遍历的根节点

返回值:因为结果已经在result中记录,因此不需要返回值。

void traversal(TreeNode* cur)
  • 确定终止条件

当cur为空值时,停止遍历。

if (cur == NULL) return;
  • 确定单层递归逻辑

    //左
    traversal(cur->left);

    //中
    if (pre == NULL) count = 1; //表示刚开始进行遍历,当前元素出现的频率为1
    else if (pre->val == cur->val) count++; //如果前一个节点的值和当前节点的值相同,这频率加1
    else count = 1;如果两个节点的值不相等,这更新频率
    pre = cur; //让pre跟在cur的后面

    if (count == maxCount) result.push_back(cur->val);//如果当前元素出现的频率和最大频率相等,则说明这个数也是前面子集中的众数并进行记录
    //不得不说点睛之笔
    if (count > maxCount){
    maxCount = count;//表明出现了更大频率的元素,更新maxCount,并清空结果集
    result.clear();
    result.push_back(cur->val);

    //右
    traversal(cur->right);
    return;

C++代码

class Solution {
private:
    TreeNode* pre = NULL;
    int count = 0;
    int maxCount = 0;
    vector<int> result;
    void traversal(TreeNode* cur){
        if (cur == NULL) return ;
        //左
        traversal(cur->left);

        //中
        if (pre == NULL) count = 1; //表示刚开始进行遍历,当前元素出现的频率为1
        else if (pre->val == cur->val) count++; //如果前一个节点的值和当前节点的值相同,这频率加1
        else count = 1;//如果两个节点的值不相等,这更新频率
        pre = cur; //让pre跟在cur的后面

        if (count == maxCount) result.push_back(cur->val);//如果当前元素出现的频率和最大频率相等,则说明这个数也是前面子集中的众数并进行记录
                                                        //不得不说点睛之笔
        if (count > maxCount){ 
            maxCount = count;//表明出现了更大频率的元素,更新maxCount,并清空结果集
            result.clear();
            result.push_back(cur->val);
        }
        //右
        traversal(cur->right);
        return;
    }
public:
    vector<int> findMode(TreeNode* root) {
        traversal(root);
        return result;
    }
};

LeetCode: 236.二叉树的最近公共祖先

力扣代码链接

文字讲解:LeetCode: 236.二叉树的最近公共祖先

视频讲解:自底向上查找,有点难度!

基本思路

对于二叉树我们大多是从上往下遍历,但是这个题目显然需要我们从下往上进行遍历,这个时候我们就应该想到需要利用回溯的方法进行求解,而后续遍历是天然的回溯过程,他先遍历左右孩子,然后根据左右孩子的值来处理中间节点。那么我们应该如何判断节点p和节点q的公共祖先是哪一个呢?有两种情况:

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

情况二:很多人容易忽略一个情况,就是节点本身p(q),它拥有一个子孙节点q(p)。

其实情况一和情况二代码实现过程都是一样的,也可以说,实现情况一的逻辑,顺便包含了情况二。因为遇到 q 或者 p 就返回,这样也包含了 q 或者 p 本身就是 公共祖先的情况。

  • 确定函数参数和返回值

参数:需要传入一个指向当前节点的指针,并且传入需要寻找的两个指向p和q节点的指针。

返回值:返回一个指向两个节点公共祖先的指针。

TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q)
  • 确定终止条件

遍历整个二叉树,当节点为空时,表示当前未找到目标节点,则返回null,如果节点为p或q,则表明找到了,返回当前节点

if(root == p || root == q) return root;
if(root == NULL) return NULL;
  • 确定单层递归逻辑

如果左子树存在p或q,则返回左孩子,同理返回右孩子,如果左右孩子都为空,则返回空,如果左右孩子都不为空,那么就说明当前节点为公共祖先!

if (left != NULL && right != NULL) return root;	//左右都不为空
else if (left == NULL && right != NULL) return right; //左空右不空
else if(left != NULL && right == NULL) return left; //右空左不空
else retuen NULL; //  (left == NULL && right == NULL)

C++代码

class Solution {
public:
    TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
        if (root == q || root == p || root == NULL) return root;
        TreeNode* left = lowestCommonAncestor(root->left, p, q);//左
        TreeNode* right = lowestCommonAncestor(root->right, p, q);//右
        if (left != NULL && right != NULL) return root;//中
        else if (left == NULL && right != NULL) return right;
        else if (left != NULL && right == NULL) return left;
        else  { //  (left == NULL && right == NULL)
            return NULL;
        }
    }
};
相关推荐
alphaTao1 小时前
LeetCode 每日一题 2024/10/28-2024/11/3
python·算法·leetcode
凡人的AI工具箱3 小时前
15分钟学 Go 第 37 天:综合复习与小项目
开发语言·后端·算法·golang
Trouvaille ~6 小时前
【C++篇】在秩序与混沌的交响乐中: STL之map容器的哲学探寻
开发语言·数据结构·c++·算法·迭代器模式·stl·map
m0_571957587 小时前
Java | Leetcode Java题解之第537题复数乘法
java·leetcode·题解
五条凪8 小时前
从零开始的LeetCode刷题日记:70. 爬楼梯
数据结构·算法·leetcode·职场和发展·1024程序员节
小丁爱养花9 小时前
算法专题:栈
数据结构·算法·leetcode
azhou的代码园9 小时前
基于SpringBoot+微信小程序+协同过滤算法+二维码订单位置跟踪的农产品销售平台-新
spring boot·算法·微信小程序
cuisidong199710 小时前
5G无线帧基本架构
网络·算法·5g
地平线开发者10 小时前
【征程 6 工具链性能分析与优化-1】编译器预估 perf 解读与性能分析
算法·自动驾驶