大连做网站仟亿科技新型实体企业100强
二叉搜索树的最小绝对差
题目链接:530. 二叉搜索树的最小绝对差 - 力扣(LeetCode)
题目描述:
给你一个二叉搜索树的根节点 root
,返回 树中任意两不同节点值之间的最小差值 。
差值是一个正数,其数值等于两值之差的绝对值。
示例 1:
输入:root = [4,2,6,1,3] 输出:1
示例 2:
输入:root = [1,0,48,null,null,12,49] 输出:1
题解:
/*** 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:int result = INT_MAX; // 初始化结果为整型最大值,用于后续比较TreeNode* pre = nullptr; // 用于记录中序遍历过程中的前一个节点// 递归遍历函数,计算BST中任意两节点的最小差值void traversal(TreeNode* cur) {// 递归终止条件:当前节点为空时直接返回if (cur == nullptr) return;// 1. 递归遍历左子树(中序遍历的第一步:左)traversal(cur->left);// 2. 处理当前节点(中序遍历的第二步:根)if (pre != nullptr) { // 如果前一个节点不为空(说明不是第一个节点)// 计算当前节点与前一个节点的差值,并更新全局最小差值result = min(result, cur->val - pre->val);}pre = cur; // 更新pre为当前节点,供下一次比较使用// 3. 递归遍历右子树(中序遍历的第三步:右)traversal(cur->right);}public:// 对外接口:返回BST中任意两节点的最小差值int getMinimumDifference(TreeNode* root) {traversal(root); // 调用递归函数遍历整棵树return result; // 返回计算得到的最小差值}
};
总结与思考:
- 此题与验证二叉搜索树逻辑相似,对应力扣98题,我们可以将给出的二叉搜索树转化为有序数组后,再进行最小绝对差的计算,或者可以直接在二叉搜索树中进行操作,我们这里直接选择在二叉搜索树中进行操作。
- 递归要注意的三个重点:
- 递归函数的参数和返回值:cur作为当前正在处理的树节点,因为该函数不需要直接返回计算结果,而是通过修改类的成员变量result和pre来记录状态,因此不需要返回值,类型为void
- 递归函数的结束条件:当递归到空节点时,说明遍历结束,退出递归
- 递归函数单层递归的逻辑:递归遍历左子树,随后进行根进行处理,再递归遍历右子树,重点为对根的处理逻辑,计算当前节点与前一个节点的差值,并更新全局的最小差值
二叉搜索树中的众数
题目链接:501. 二叉搜索树中的众数 - 力扣(LeetCode)
题目描述:
给你一个含重复值的二叉搜索树(BST)的根节点 root
,找出并返回 BST 中的所有 众数(即,出现频率最高的元素)。
如果树中有不止一个众数,可以按 任意顺序 返回。
假定 BST 满足如下定义:
- 结点左子树中所含节点的值 小于等于 当前节点的值
- 结点右子树中所含节点的值 大于等于 当前节点的值
- 左子树和右子树都是二叉搜索树
示例 1:
输入:root = [1,null,2,2] 输出:[2]
示例 2:
输入:root = [0] 输出:[0]
题解:
/*** 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:int maxCount = 0;//最大频率int count = 0;//统计频率TreeNode* pre = nullptr;//指向前一个元素vector<int> result;//统计结果的数组void searchBST(TreeNode* cur){if(cur == nullptr) return;//按照左中右的顺序进行递归和处理searchBST(cur->left);//重点处理“中”的逻辑if(pre == nullptr){//第一个节点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){//如果计数大于最大频率maxCount = count;//更新最大频率result.clear();//若count>maxCount,则之前统计的result失效,需要清空result.push_back(cur->val);//放入到结果数组中}searchBST(cur->right);return;}
public:vector<int> findMode(TreeNode* root) {int count = 0;int maxCount = 0;TreeNode* pre = nullptr;//记录前一个节点result.clear();searchBST(root);return result;}
};
总结与思考:
- 因为是寻找二叉搜索树中的众数,则其中序遍历一定是有序的,根据这一点我们可以使用中序遍历递归解法
- 该递归函数通过修改类的成员变量result和pre来记录状态,我们可以使用pre和cur指针的技巧,弄一个指针指向前一个节点,这样每次cur(当前节点)才能和pre(前一个节点)作比较。而且初始化的时候pre = NULL,这样当pre为NULL时候,我们就知道这是比较的第一个元素。
- 随后就是常规的中序递归的写法,但这里有一个重难点,我们如何将统计出来的众数作为一个结果数组result返回出来,我们可以遍历一次数组,找出其中最大频率对应的结果,将这个结果再遍历放入集合中,但这样效率不高,所以我们需要在处理“中”节点时做一些额外的处理
- 若找出的统计频率等于最大统计频率,则将这个频率对应下的元素直接放入结果集合中,若找出的统计频率大于最大统计频率,则更新最大统计频率后,将result数组清空,随后再重新将该频率对应下的元素放入结果集合中。这样我们就可以做到只需要遍历一次二叉搜索树,就找出了众数的集合
二叉树的最近公共祖先
题目链接:236. 二叉树的最近公共祖先 - 力扣(LeetCode)
题目描述:
给定一个二叉树, 找到该树中两个指定节点的最近公共祖先。
百度百科中最近公共祖先的定义为:“对于有根树 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
题解:
/*** 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) {//递归结束条件,找到q或p或为空时,返回对应结果if(root == q || root == p || root == NULL) return root;//采用后序遍历,左右中的顺序处理//递归左TreeNode* left = lowestCommonAncestor(root->left, q, p);//递归右TreeNode* right = lowestCommonAncestor(root->right, q, p);//递归中//左右都找到的情况if(left != NULL && right != NULL) return root;//其他情况if(left != NULL && right == NULL) return left;else if(left == NULL && right != NULL) return right;else{//左右都没找到return NULL;}}
};
总结与思考:
- 寻找目标的最近公共祖先,我们需要自底向上遍历,因此这里利用回溯的特性:自底向上,而后序遍历的顺序,左右中就是天然的自底向上的过程,因此使用后序遍历递归来解题。
- 递归需要注意的三个要点:
- 递归函数的参数和返回值:我们需要寻找q,p节点的公共祖先root,因此参数为这三个树节点,返回值类型代表着是否找到了公共祖先,因此使用bool型
- 递归函数的结束条件:根节点为空的情况下说明是空树,直接返回,若我们找到了p,q时,直接返回节点值。
- 递归函数的单层递归逻辑:因为回溯的过程需要递归函数的返回值做判断,所以我们需要遍历整棵树的所有节点,具体的遍历逻辑体现在代码中