501 二叉搜索树中的众数 easy
这道题中定义的二叉搜索树与此前的不同,题目要求如下:
给你一个含重复值的二叉搜索树(BST)的根节点 root ,找出并返回 BST 中的所有 众数(即,出现频率最高的元素)。
如果树中有不止一个众数,可以按 任意顺序 返回。
假定 BST 满足如下定义:
- 结点左子树中所含节点的值 小于等于 当前节点的值
- 结点右子树中所含节点的值 大于等于 当前节点的值
- 左子树和右子树都是二叉搜索树
通常我们会想,定义一个哈希数组存储遍历到的每个值出现的次数,最终返回其中出现频率最大的数的集合就好,但这样就会用到额外的空间。所以,这道题的难度就在于不使用额外的空间。只用一个数组,记录树中众数的集合。
递归时,还是要使用中序遍历,才能更好的适应二叉搜索树的特性,
用一个数记录每个数值出现的次数,一个数记录最大的出现频率,
用一个结点类型记录此前遇到的结点,
用一个数组作为最终的结果,
如果当前数值出现的次数超过最大频率,就清空数组,并把新的数值加入数组中,代码如下
private:
int maxCount = 0;
int count = 0;
TreeNode *pre = nullptr;
vector<int> res;
void searchBST(TreeNode *cur) {
if (!cur) return;
searchBST(cur->left);
if (pre == nullptr) {
count = 1;
}else if (pre->val == cur->val) {
count++;
}else {
count = 1;
}
pre = cur;
if (count == maxCount) {
res.push_back(cur->val);
}
if (count > maxCount) {
maxCount = count;
res.clear();
res.push_back(cur->val);
}
searchBST(cur->right);
return;
}
public:
vector<int> findMode(TreeNode* root) {
count = 0;
maxCount = 0;
TreeNode *pre = nullptr;
res.clear();
searchBST(root);
return res;
}
236 二叉树的最近公共祖先 medium
给定一个二叉树, 找到该树中两个指定节点的最近公共祖先。
百度百科中最近公共祖先的定义为:“对于有根树 T 的两个节点 p、q,最近公共祖先表示为一个节点 x,满足 x 是 p、q 的祖先且 x 的深度尽可能大(一个节点也可以是它自己的祖先)。”
还有三个较为关键的信息:
- 所有
Node.val
互不相同
。p != q
p
和q
均存在于给定的二叉树中。
这道题的关键点在于如何遍历到两个目标节点,且找到最近公共祖先
乍一想,情况蛮多蛮复杂的,但实际上并不是。我们假想两个目标结点的分布情况,一种是两个结点分别分布在公共祖先结点的左右子树,另一种就是两个结点之一就是公共祖先。
因此,采用递归处理的时候,返回类型是结点类型,表示是否找到的目标结点是否为空,返回条件就是是否遍历到两个结点,或者是结点为空;
每一次递归,都要从当前结点的左右两个结点开始,通过返回值判断,
如果左右子树都非空,表示两个结点就在当前结点的左右子树;
如果其中之一为空,就表示当前结点就是公共祖先;
如果都是空,就说明只有根节点(解题中应该也就一种情况满足该条件)
综上所述,代码如下:
TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
if (root == q || root == p || root == nullptr) return root;
TreeNode* left = lowestCommonAncestor(root->left, p, q);
TreeNode* right = lowestCommonAncestor(root->right, p, q);
if (left != nullptr && right != nullptr) return root;
else if (left == nullptr && right != nullptr) return right;
else if (left != nullptr && right == nullptr) return left;
return nullptr;
}
235 二叉搜索树的最近公共祖先 medium
给定一个二叉搜索树, 找到该树中两个指定节点的最近公共祖先。
这道题与上一题的区别就在于可以利用二叉搜索树的特性进行剪枝操作。
具体应该如何操作呢?
我们还是回到上一题中所讲的两种情况:
- 两个结点分别分布在公共祖先结点的左右子树
- 两个结点之一就是公共祖先
而二叉搜索树是一棵有序树,结点的数值就是优化的方向。
对于第一种情况,两个结点分别分布在公共祖先结点的左右子树
,说明公共祖先的数值一定位于两个目标结点之间;对于第二种情况,两个结点之一就是公共祖先
,说明公共祖先结点数值就在区间[p,q]
的两端之一。
综上所述,如果遇到结点数值在[p,q]
范围内,就说明已找到公共祖先,且一定是最近的(第一次遇到就可以返回),代码如下:
TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
while (root) {
if (root->val > p->val && root->val > q->val) {
root = root->left; //大于[p,q]区间,要往较小值的方向遍历
} else if (root->val < p->val && root->val < q->val) {
root = root->right; //小于[p,q]区间,要往较大值的方向遍历
} else
return root;
}
return root;
}
此外,这道题还可以用递归处理,和迭代的逻辑一致,代码也很简单:
TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
if (root->val > p->val && root->val > q->val)
return lowestCommonAncestor(root->left, p, q);
if (root->val < p->val && root->val < q->val)
return lowestCommonAncestor(root->right, q, p);
return root;
}