文章目录
前置知识
参考前文
参考文章:
LeetCode刷题笔记【9】:二叉树专题-1(分别用递归遍历、迭代遍历、标记遍历实现前、中、后序遍历)
LeetCode刷题笔记【10】:二叉树专题-2(二叉树的层序遍历、翻转二叉树、对称二叉树)
LeetCode刷题笔记【10.5】:二叉树专题-2.5(二叉树的层序遍历 - 10道题)
LeetCode刷题笔记【11】:二叉树专题-3(二叉树的最大深度、二叉树的最小深度、完全二叉树的节点个数)
LeetCode刷题笔记【12】:二叉树专题-4(平衡二叉树、二叉树的所有路径、左叶子之和)
LeetCode刷题笔记【13】:二叉树专题-5(找树左下角的值 、路径总和、从前序与中序遍历序列构造二叉树、从中序与后序遍历序列构造二叉树)
LeetCode刷题笔记【14】:二叉树专题-6(最大二叉树 、合并二叉树 、二叉搜索树中的搜索 、验证二叉搜索树)
530.二叉搜索树的最小绝对差
题目描述
LeetCode链接:https://leetcode.cn/problems/minimum-absolute-difference-in-bst/description/
遍历生成数组, sort(通用, 不针对二叉搜索树)
思路: 遍历一遍, 生成数组, sort数组, 遍历求最小差
class Solution {
private:
vector<int> marker;
public:
void traversal(TreeNode* root){
if(root==nullptr)
return;
marker.push_back(root->val);
traversal(root->left);
traversal(root->right);
return;
}
int getMinimumDifference(TreeNode* root) {
traversal(root);
sort(marker.begin(), marker.end());
int ans=INT_MAX;
for(int i=0; i<marker.size()-1; ++i){
ans = min(ans, marker[i+1]-marker[i]);
}
return ans;
}
};
利用二叉搜索树性质, 中序遍历生成数组
以上方法虽然可行, 但是没有使用二叉搜索树的性质;
因为是二叉搜索树, 所以只要以中序遍历, 形成的序列就是递增的
class Solution {
private:
vector<int> marker;
public:
void traversal(TreeNode* root){
if(root==nullptr)
return;
traversal(root->left);
marker.push_back(root->val);
traversal(root->right);
return;
}
int getMinimumDifference(TreeNode* root) {
traversal(root);
int ans=INT_MAX;
for(int i=0; i<marker.size()-1; ++i){
ans = min(ans, marker[i+1]-marker[i]);
}
return ans;
}
};
利用二叉搜索树性质, 且不用数组额外空间
其实不用在过程中维护marker
数组, 只需要用int pre
来记录在序列中的前一个节点的val
;
具体的操作是最开始让pre=-1
, 之后每次都pre=-1
时让pre=root->val
;
注意过程中传递的是pre
和ans
的引用;
class Solution {
public:
void traversal(TreeNode* root, int& ans, int& pre){
if(root==nullptr)
return;
traversal(root->left, ans, pre);
if(pre == -1){
pre = root->val;
}else{
ans = min(ans, root->val-pre);
pre = root->val;
}
traversal(root->right, ans, pre);
}
int getMinimumDifference(TreeNode* root) {
int ans=INT_MAX, pre=-1;
traversal(root, ans, pre);
return ans;
}
};
501.二叉搜索树中的众数
题目描述
LeetCode链接:https://leetcode.cn/problems/find-mode-in-binary-search-tree/description/
用unordered_map统计出现频率
用unordered_map<int, int>
记录每个数出现的次数, 看最大值
class Solution {
private:
unordered_map<int,int> map;
public:
void dfs(TreeNode* root){
if(root==nullptr)
return;
map[root->val]++;
dfs(root->left);
dfs(root->right);
return;
}
vector<int> findMode(TreeNode* root) {
dfs(root);
int maxNum=INT_MIN;
for(auto& pair : map){
maxNum = max(maxNum, pair.second);
}
vector<int> ans;
for(auto& pair : map){
if(pair.second==maxNum){
ans.push_back(pair.first);
}
}
return ans;
}
};
利用二叉搜索树性质进行遍历
那么能不能和<530. 二叉搜索树的最小绝对差>中一样
使用pre
和cur
进行遍历呢?
过程中检查cur->val
和pre->val
是否一样, 如果一样则counter++
, 不一样则counter=1
;
且过程中维护vector<int> ans
和 int maxNum
;
当counter==maxNum
的时候, 在ans
中加入cur->val
;
当counter>maxNum
的时候, 清空ans
, 并加入cur->val
, 更新maxNum
;
及时用cur
替换pre
。
class Solution {
private:
vector<int> ans;
int maxNum=0, counter=0;
TreeNode* pre=nullptr;
void traversal(TreeNode* cur){
if(cur==nullptr)
return;
traversal(cur->left);
if(pre==nullptr){
counter = 1;
}else if(pre->val == cur->val){
counter++;
}else if(pre->val != cur->val){
counter = 1;
}
if(counter == maxNum){
ans.push_back(cur->val);
}else if(counter > maxNum){
ans.clear();
ans.push_back(cur->val);
maxNum = counter;
}
pre = cur;
traversal(cur->right);
return;
}
public:
vector<int> findMode(TreeNode* root) {
traversal(root);
return ans;
}
};
236. 二叉树的最近公共祖先
题目描述
LeetCode链接:https://leetcode.cn/problems/lowest-common-ancestor-of-a-binary-tree/
分别搜索两个节点, 生成搜索序列, 比较搜索序列
思路①: 分别查找两个节点, 生成两个查找队列, 然后检查两个队列, 从哪个节点开始不一样了, 那个节点的上一个节点就是"最近公共祖先"
class Solution {
private:
vector<TreeNode*> path;
public:
bool dfs(TreeNode* root, TreeNode* target, vector<TreeNode*>& path){
if(root==NULL)
return false;
if(root==target){
path.push_back(root);
return true;
}
path.push_back(root);
if(dfs(root->left, target, path) || dfs(root->right, target, path)){
return true;
}
path.pop_back();
return false;
}
TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
vector<TreeNode*> pathP, pathQ;
dfs(root, p, pathP);
dfs(root, q, pathQ);
int i;
for(i=0; i<pathP.size()&&i<pathQ.size(); ++i){
if(pathP[i]!=pathQ[i]){
break;
}
}
return pathP[i-1];
}
};
回溯法
如上图所示, 参考后序遍历的方式对整棵树进行递归的后序遍历.
每一次递归返回NULL或者相应的查找成功节点.
class Solution {
public:
TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
if(root==NULL || p==root || q==root)
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;
if(left!=NULL && right==NULL)
return left;
// if(left==NULL && right==NULL)
return NULL;
}
};
总结
对于二叉搜索树性质的使用: 中序遍历即可获得递增序列
回溯法的顺序: 参考后序遍历可类比
对于二叉搜索树的性质, 以及回溯法, 还不是很熟练.
但是回溯法的使用相关题目似乎还挺常见的, 需要加强练习(后续似乎有专门的回溯专题)
本文参考:
二叉搜索树的最小绝对差
二叉搜索树中的众数
二叉树的最近公共祖先