代码随想录算法训练营第二十天|Leetcode530 二叉搜索树的最小绝对差、Leetcode501 二叉搜索树中的众数、Leetcode236 二叉树的最近公共祖先
● Leetcode530 二叉搜索树的最小绝对差
题目链接:Leetcode530 二叉搜索树的最小绝对差
视频讲解:代码随想录|二叉搜索树的最小绝对差
题目描述:给你一个二叉搜索树的根节点 root ,返回 树中任意两不同节点值之间的最小差值 。
差值是一个正数,其数值等于两值之差的绝对值。
示例 1:
输入:root = [4,2,6,1,3]
输出:1
示例 2:
输入:root = [1,0,48,null,null,12,49]
输出:1
提示:
树中节点的数目范围是 [2, 104]
0 <= Node.val <= 105
● 解题思路
方法一:暴力求解
因为二叉排序树中序遍历结果按照递增排列,我们可以先将二叉排序树中序遍历结果存储在数组中,再遍历数组的过程中比较取得最小差值;
方法二:递归
依照递归三部曲进行:
(1)确定递归函数参数和返回值:
我们定义了全局变量result
存储最小差,因此不需要设置任何返回值,传入参数仅传入结点cur
即可;
(2)确定递归函数的终止条件:
当遍历结点为空时,我们需要向递归的上一层返回;
(3)确定单层遍历逻辑:
选择中序遍历
能最好体现二叉搜索树的特性,也能帮助我们寻找相邻结点的最小差值;
在整个递归的过程,cur
会自动地按照递归地顺序遍历,如图中红色路线,因此其前驱结点pre
也需要跟随着cur
移动,如蓝色路线:
方法三:迭代
迭代法和递归一样,cur
和pre
都按照中序顺序一直走,只不过是使用栈模拟递归。
● 代码实现
方法一:暴力求解
/**
* 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 {
public:
vector<int> vec;
void traversal(TreeNode* root)
{
if(!root) return;
traversal(root->left);
vec.push_back(root->val);
traversal(root->right);
}
int getMinimumDifference(TreeNode* root) {
traversal(root);
int result = INT_MAX;
for(int i = 0; i < vec.size() - 1; i++)
{
if(vec[i + 1] - vec[i] < result)
{
result = vec[i + 1] - vec[i];
}
}
return result;
}
};
方法二:递归
/**
* 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 {
public:
int result = INT_MAX;
TreeNode* pre = nullptr; //存储按照中序遍历cur的前一个结点
void traversal(TreeNode* cur)
{
//终止条件
if(!cur) return;
traversal(cur->left); //左
if(pre) result = min(result, cur->val - pre->val); //中
pre = cur; //移动pre
traversal(cur->right);
}
int getMinimumDifference(TreeNode* root) {
traversal(root);
return result;
}
};
方法三:迭代
/**
* 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 {
public:
int getMinimumDifference(TreeNode* root) {
stack<TreeNode*> st;
TreeNode* cur = root;
TreeNode* pre = NULL;
int result = INT_MAX;
while (cur != NULL || !st.empty()) {
if (cur != NULL) { // 指针来访问节点,访问到最底层
st.push(cur); // 将访问的节点放进栈
cur = cur->left; // 左
} else {
cur = st.top();
st.pop();
if (pre != NULL) { // 中
result = min(result, cur->val - pre->val);
}
pre = cur;
cur = cur->right; // 右
}
}
return result;
}
};
● Leetcode501 二叉搜索树中的众数
题目链接:Leetcode501 二叉搜索树中的众数
视频讲解:代码随想录|二叉搜索树中的众数
题目描述:给你一个含重复值的二叉搜索树(BST)的根节点 root ,找出并返回 BST 中的所有 众数(即,出现频率最高的元素)。
如果树中有不止一个众数,可以按 任意顺序 返回。
假定 BST 满足如下定义:
结点左子树中所含节点的值 小于等于 当前节点的值
结点右子树中所含节点的值 大于等于 当前节点的值
左子树和右子树都是二叉搜索树
示例 1:
输入:root = [1,null,2,2]
输出:[2]
示例 2:
输入:root = [0]
输出:[0]
提示:
树中节点的数目在范围 [1, 104] 内
-105 <= Node.val <= 105
● 解题思路
因为是二叉排序树,遍历规则我们依然选择中序遍历
。
整体思路:
我们分别使用cout、maxCount和pre
记录在遍历过程中当前元素出现的次数、当前遍历过元素众数出现的最大值和前驱结点(前驱结点帮助我们判断前一个元素和当前元素是否相等,对count进行相应操作);
我们会遇到以下情况:
(1)pre
为空时,遍历元素第一次出现,count置为1;
(2)pre->val == cur->val
,当前元素出现次数加一;
(3)pre->val == cur->val
,当前元素改变,count重新置为1。
在这个过程中我们需要注意:当count == maxCount
时,将当前元素插入返回数组中,但会产生疑问:我们在开始maxCount没有进行赋值,记录的也并非是最终最大值,因此我们下一步就是需要对其进行处理;
当count > maxCount
,即当前元素出现频率大于之前遍历元素最大出现频率,证明返回数组的所有元素不满足条件,我们将其清空并将新的最大频率赋值。
以上操作便是完整的对于二叉排序树众数的处理,同时只遍历一次。
● 代码实现
方法一:递归
/**
* 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:
vector<int> res; //存储返回结果
int count = 0; //记录当前元素出现个数
int maxCount = 0; //记录最大元素出现次数
TreeNode* pre = NULL; //中序遍历的前驱结点
void traversal(TreeNode* cur)
{
//终止条件
if(!cur) return;
traversal(cur->left); //左
//中
if(!pre) count = 1;
else if(pre->val == cur->val) count++;
else count = 1; //pre->val != cur->val
pre = cur;//移动pre
if(count == maxCount)
{
res.push_back(cur->val);
}
if(count > maxCount)
{
maxCount = count;
res.clear();
res.push_back(cur->val);
}
traversal(cur->right); //右
return;
}
public:
vector<int> findMode(TreeNode* root) {
count = 0;
maxCount = 0;
pre = NULL;
res.clear();
traversal(root);
return res;
}
};
方法二:迭代
/**
* 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 {
public:
vector<int> findMode(TreeNode* root) {
int count = 0, maxCount = 0;
TreeNode* pre = nullptr;
vector<int> result;
stack<TreeNode*> st;
TreeNode* cur = root;
while(cur || !st.empty())
{
if(cur != nullptr)
{
st.push(cur);
cur = cur->left;
}
else
{
cur = st.top(); st.pop();
if(pre == nullptr) 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();
maxCount = count;
result.push_back(cur->val);
}
pre = cur;
cur = cur->right;
}
}
return result;
}
};
● Leetcode236 二叉树的最近公共祖先
题目链接:Leetcode236 二叉树的最近公共祖先
视频讲解:代码随想录|二叉树的最近公共祖先
题目描述:给定一个二叉树, 找到该树中两个指定节点的最近公共祖先。
百度百科中最近公共祖先的定义为:“对于有根树 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
提示:
·树中节点数目在范围 [2, 105] 内。
·-109 <= Node.val <= 109
·所有 Node.val 互不相同 。
·p != q
·p 和 q 均存在于给定的二叉树中。
● 解题思路
对于本题,我们需要先判断左右子树中是否有结点满足left/right == p/q
,然后将结果返回到中结点,对于先左右后中的遍历方式,本题选择后序遍历
。
对于本题的终止条件:当传入结点为空或者传入结点就是我们的p/q
,直接返回结点;
本题符合返回条件的情况为:
当左右结点不为空,即p和q一定在左右子树中,返回当前结点;
但或许会有疑问:万一在左右子树中都有p
或者都有q
,也会返回当前结点,这就需要注意在本题的提示中:
所有 Node.val 互不相同
而在对中结点的处理中,我们需要判断:
(1)当左结点返回值为空,右结点返回值不为空,我们将右结点返回值向上一次递归返回即可;
(2)当左结点返回值不为空,右结点返回值为空,我们将左结点返回值向上一次递归返回即可;
(3)否则左右结点均没有q & p
,返回空即可。
● 代码实现
递归
/**
* 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) {
//终止条件
if(!root) return nullptr;
if(p == root || q == root) return root;
TreeNode* left = lowestCommonAncestor(root->left, p, q); //左
TreeNode* right = lowestCommonAncestor(root->right, p, q); //右
//中
if(left && right) return root;
if(!left && right) return right;
else if(left && !right) return left;
else return nullptr;
}
};