大家好!今天我们来到了二叉树的新的章节,昨天的二叉搜索树大家理解的如何,那么我们就直接开始我们今天的内容。
第一题对应力扣编号为530的题目二叉搜索树的最小绝对差
看到这道题我感觉应该是不难的,不就是求出所有节点的数值最小差值的绝对值吗?
最直观的想法,就是把二叉搜索树转换成有序数组,然后遍历一遍数组,就统计出来最小差值了。我们首先来写一下这一种思路的代码:
class Solution {
private:
vector<int> vec;
//其实我感觉我们用哪一种遍历都是可以的,但是基于二叉搜索树的特殊性质我们还是使用中序遍历
void traversal(TreeNode *root)
{
if (root == NULL) return;
if (root -> left) traversal(root -> left);
vec.push_back(root -> val);
if (root -> right) traversal(root -> right);
}
public:
int getMinimumDifference(TreeNode* root) {
int result = INT_MAX;
vec.clear();
traversal(root);
if (vec.size() < 2) return 0;
for (int i = 1; i < vec.size(); ++i)
{
result = min(result, vec[i] - vec[i - 1]);
}
return result;
}
};
这段代码想必大家都很明白了,递归遍历与数组遍历求差值,那这道题目是否还有其他解法呢?其实是有的,我们其实也可以一边递归一边记录当前节点的上一个节点,那其实这个思路也是很简单的,大家看一下代码:
class Solution {
private:
int result = INT_MAX;
TreeNode *pre = NULL;//记录当前节点的上一个节点
void trversal(TreeNode *cur)
{
if (cur == NULL) return;
trversal(cur -> left);//左
if (pre != NULL)
{
result = min(result, cur -> val - pre -> val);
}
pre = cur;//千万不要忘记赋值也就是更新前节点
trversal(cur -> right);//右
}
public:
int getMinimumDifference(TreeNode* root) {
trversal(root);
return result;
}
};
这种解法也不难理解,大家注意我们还是遵循中序遍历,同时要记得更新前节点这样我们才能继续去递归求解最小的差值。这道题我就给大家讲解这么多,接下来我们看下一道题。
第二题对应力扣编号501的题目二叉搜索树中的众数
其实题目要求应该是很简单的:
就是找到众数并保存在一个数组里然后返回就可以了,其实大家还记得我们前几个章节里面的哈希表吗?我们需要哈希表的数据结构来帮助我们解决这道题目,我们首先需要统计元素的出现次数,但这里我们要找出现频率最大的那么就适合使用unordered_map了,因为后面会自定义排序函数的,我们使用递归找出所有元素的出现频率之后,我们还需要自定义排序规则,我们是要按照元素出现的频率来排序,最后我们就可以找到出现频率最高的,代码比较复杂,大家可以自行参考一下:
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);
}
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;
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)
{
//看看还有没有出现频率一样的元素
if (vec[i].second == vec[0].second) result.push_back(vec[i].first);
else break;//不可能会有了因为我是按照顺序排的越往后频率越小了
}
return result;
}
};
大家注意我自定义的排序函数,以及我的排序后的vector以及最后我是如何寻找出现频率最高的元素的,代码写的很清晰,相信大家可以看明白,这道题我们就讲解到这里,继续今天的下一道题目。
第三题对应力扣编号为236的题目二叉树的最近公共祖先
首先提到最近公共祖先大家或许很陌生,其实打过蓝桥杯比赛的我倒是听说过右最近公共祖先这个知识点,其实我也没怎么系统深入学习,那么今天借助这道题目我们就认真学一下最近公共祖先究竟是什么?
其实题目给出的解释很清楚了,要是公共父节点深度还要尽可能的大,那么我们明白了什么叫做最近公共祖先,那么我们应该如何去解决这道题目呢?
遇到这个题目首先想的是要是能自底向上查找就好了,这样就可以找到公共祖先了。那如何回溯呢?其实大家可以想到后续遍历不就是从下往上的过程吗?
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;
if (left == NULL && right != NULL) return right;
else if (left != NULL && right == NULL) return left;
else { // (left == NULL && right == NULL)
return NULL;
}
}
};
大家来看一下代码,其实我们存在两种情况,第一种就是我如果发现了当前节点存在p或者q的情况我就直接返回,那我接下来使用后序遍历,我们要来看看左右子树,很明显如果当前节点的左右子树都不为空,那其实当前这个节点就是最近公共祖先,p和q已经存在于左子树或者右子树,还有几种情况,就是如果左不为空右为空的话,说明右子树里面不存在p或者q,我们这时候就将左子树往上返回,如果左为空右不为空的话,说明左子树里面不存在p或者q,我们这时候就将右子树往上返回,最后一种情况如果我们左右子树都为空就说明我们压根没找到最近共工作祖先或者可以说当前节点与p, q没有关系,其实我们还是使用的后序遍历,我们是从下网上递归直到找到左右子数都不为空的时候才能找到最近公共祖先,其实如果存在p是q祖先的情况我们得代码也是可以处理的,其实都包含了,
这是代码随想录的总结篇,其实我也感觉题目有难度,包括大家要搞明白我是如何回溯的,以及我们无论有没有找到最近公共祖先我们都要遍历完这棵树,这道题我就先讲解到这里。
总结
今天的题目最后这一道最近公共祖先比较难,前面的两道其实还是比较简单的,都是二叉搜索树的性质,捎带着复习了前面的哈希表的知识,好,今天的题目我们就讲解到这里,我们下一次打卡再见!