题目链接: 530.二叉搜索树的最小绝对差
题干: 给你一个二叉搜索树的根节点 root ,返回树中任意两不同节点值之间的最小差值 。
差值是一个正数,其数值等于两值之差的绝对值。
思考一: 中序遍历递归法。设置两个成员变量pre记录上一个节点,result记录最小差值。在pre不为空的情况处理当前递归节点,比较当前节点 val值和前一个节点pre的val值的差值与result,更新result和pre。
代码:
class Solution {
public:
TreeNode* pre = nullptr; //记录上一个节点
int result = __INT_MAX__; //记录最小差值
void traversal(TreeNode* node) {
if (!node) return;
traversal(node->left);
if (pre)
result = min(result, node->val - pre->val);
pre = node; //更新节点
traversal(node->right);
}
int getMinimumDifference(TreeNode* root) {
traversal(root);
return result;
}
};
思考二: 中序遍历迭代法。在中序遍历迭代写法的基础上简单修改循环中出栈元素后的处理逻辑:改为更新最小差值和记录节点
中序遍历迭代写法:第十四天| 二叉树的递归遍历、二叉树的迭代遍历、二叉树的统一迭代法
代码:
class Solution {
public:
int getMinimumDifference(TreeNode* root) {
stack st;
TreeNode* cur = root;
TreeNode* pre = nullptr;
int result = __INT_MAX__;
while (!st.empty() || cur) {
if (cur) {
st.push(cur);
cur = cur->left; //左 指针访问到最左下
} else {
cur = st.top(); st.pop(); //栈顶存放着左孩子为空或者左孩子已经访问过的节点
if (pre)
result = min(result, cur->val - pre->val); //中
pre = cur;
cur = cur->right; //右
}
}
return result;
}
};
思考三: 中序遍历统一迭代法。在中序遍历统一迭代写法的基础上简单修改循环中的处理逻辑:改为更新最小差值和记录节点
中序遍历统一迭代写法:第十四天| 二叉树的递归遍历、二叉树的迭代遍历、二叉树的统一迭代法
代码:
class Solution {
public:
int getMinimumDifference(TreeNode* root) {
stack st;
int result = __INT_MAX__;
if (!root) return result;
TreeNode* cur = root;
TreeNode* pre = nullptr;
st.push(root);
while (!st.empty()) {
TreeNode* cur = st.top();
if (cur != nullptr) {
st.pop();
if (cur->right != nullptr) st.push(cur->right); //右
st.push(cur); //中
st.push(nullptr);//null标志 说明后面的节点访问过
if (cur->left != nullptr) st.push(cur->left); //左
} else { //遇到null统一输出栈中后一个元素
st.pop();
cur = st.top();
st.pop();
if (pre)
result = min(result, cur->val - pre->val); //中
pre = cur;
}
}
return result;
}
};
题目链接:501 二叉搜索树中的众数
题干:给你一个含重复值的二叉搜索树(BST)的根节点
root
,找出并返回 BST 中的所有 众数(即,出现频率最高的元素)。如果树中有不止一个众数,可以按 任意顺序 返回。假定 BST 满足如下定义:
- 结点左子树中所含节点的值 小于等于 当前节点的值
- 结点右子树中所含节点的值 大于等于 当前节点的值
- 左子树和右子树都是二叉搜索树
思考一:按普通二叉树来解题。先通过二叉树遍历(前中后,层序均可)统计各元素出现频率。接着给容器内元素进行排序,最后将同排序后的首个元素出现频率相同的元素添加到最终结果内。
代码:
class Solution {
public:
//前序遍历
void traversal(TreeNode* node, unordered_map& map) {
if (!node) return;
map[node->val]++; //统计频率
traversal(node->left, map);
traversal(node->right, map);
}
//比较规则
bool static cmp (const pair& a, const pair& b) {
return a.second > b.second;
}
vector findMode(TreeNode* root) {
unordered_map map; //key:元素值 value:出现频率
vector result;
if (!root) return result;
traversal(root, map);
vector> 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;
}
};
思考二:按二叉搜索树考虑递归法解题。
中间节点处理逻辑的关键点:
代码:
class Solution {
public:
TreeNode* pre = nullptr;
vector result;
int maxCount = 0; //统计最大频率
int count = 0; //元素出现频率
void traversal(TreeNode* node) {
if (!node) return;
traversal(node->left);
if (!pre) count = 1; //首个元素设置频率初始值
else if (pre->val == node->val) count++; //前一个节点和当前节点的相同则频率增一
else count = 1; //前一个节点和当前节点的不相同则重新设置频率初始值
pre = node;
if (count == maxCount) result.push_back(node->val); //频率相同则加入容器
if (count > maxCount) { //当前元素出现频率高于记录频率
maxCount = count;
result.clear(); //清空前面记录的元素
result.push_back(node->val);
}
traversal(node->right);
}
vector findMode(TreeNode* root) {
result.clear();
traversal(root);
return result;
}
};
思考三:按二叉搜索树考虑迭代法解题。中间节点处理逻辑关键点几乎无异。( 统一迭代法也一样,下面只给出迭代写法)
中序遍历迭代写法:第十四天| 二叉树的递归遍历、二叉树的迭代遍历、二叉树的统一迭代法
代码:
class Solution {
public:
vector findMode(TreeNode* root) {
stack st;
TreeNode* cur = root;
TreeNode* pre = nullptr;
int count = 0; //统计频率
int maxCount = 0; //最大频率
vector result;
while(cur != nullptr || !st.empty()) {
if (cur != nullptr) {
st.push(cur);
cur = cur->left; //左 指针访问到最左下
} else {
cur = st.top(); //栈顶存放着左孩子为空或者左孩子已经访问过的节点
st.pop();
if (!pre) 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(); //清空前面记录的元素
result.push_back(cur->val);
}
cur = cur->right; //右
}
}
return result;
}
};
题目链接:236 二叉树的最近公共祖先
题干:给定一个二叉树, 找到该树中两个指定节点的最近公共祖先。
百度百科中最近公共祖先的定义为:“对于有根树 T 的两个节点 p、q,最近公共祖先表示为一个节点 x,满足 x 是 p、q 的祖先且 x 的深度尽可能大(一个节点也可以是它自己的祖先)。”
- 所有节点的值都是唯一的。
- p、q 为不同节点且均存在于给定的二叉树中。
思考:递归法。考虑到查询公共祖先节点想到从底向上查询,二叉树的回溯过程就是从底往上的过程。后序遍历(左右中)就是天然的回溯过程,可以根据左右子树的返回值,来处理中节点的逻辑。
如何判断一个节点是节点q和节点p的公共祖先(两种情况):
代码:
class Solution {
public:
TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
if (!root) return nullptr;
if (root == p || root == q) return root; //遍历到目标节点
TreeNode* left = lowestCommonAncestor(root->left, p, q);
TreeNode* right = lowestCommonAncestor(root->right, p, q);
if (left && right) return root; //查找到公共祖先
else if (left && !right) return left; //左子树查找到目标节点
else if (!left && right) return right; //右子树查找到目标节点
else return nullptr;
}
};
自我总结: