在此非常感谢“代码随想录”的通俗易懂的总结!
(致敬叶师傅和李小龙)
“我不害怕曾经练过一万种踢法的人,但我害怕一种踢法练过一万次的人”(by 叶师傅的徒弟Bruce Lee)
今天继续打5道,加油,冲冲冲!
题目描述:
给定二叉搜索树(BST)的根节点和一个值。 你需要在BST中找到节点值等于给定值的节点。 返回以该节点为根的子树。 如果节点不存在,则返回 NULL。
二叉搜索树的遍历方式,因为二叉搜索树的有序性,遍历的时候要比普通二叉树简单很多。 所以针对二叉搜索树的题目,一定要利用其特性。
方法一、递归方法
class Solution {
public:
TreeNode* searchBST(TreeNode* root, int val) {
if(root==NULL || root->val==val) return root;
else if(root->val>val) return searchBST(root->left,val);
else if(root->val<val) return searchBST(root->right,val);
return NULL;
}
};
方法二、迭代方法
class Solution {
public:
TreeNode* searchBST(TreeNode* root, int val) {
while(root){
if(root->val>val) root=root->left;
else if(root->val<val) root=root->right;
else return root;
}
return NULL;
}
};
题目描述:
给定一个二叉树,判断其是否是一个有效的二叉搜索树。
假设一个二叉搜索树具有如下特征:
节点的左子树只包含小于当前节点的数。
节点的右子树只包含大于当前节点的数。
所有左子树和右子树自身必须也是二叉搜索树。
在中序遍历下,输出的二叉搜索树节点的数值是有序序列。
有了这个特性,验证二叉搜索树,只要比较一下,这个数组是否是有序的就可以了
class Solution {
public:
vector<int> vec;
void traversal(TreeNode* root){
if(root==NULL) return;
traversal(root->left);
vec.push_back(root->val);
traversal(root->right);
}
bool isValidBST(TreeNode* root) {
traversal(root);
for(int i=1;i<vec.size();++i){
if(vec[i-1]>=vec[i]) return false;
}
return true;
}
};
题目描述:
给你一棵所有节点为非负值的二叉搜索树,请你计算树中任意两节点的差的绝对值的最小值。
二叉搜索树采用中序遍历,其实就是一个有序数组。最小差值一定在相邻两个节点中间产生
方法一、递归方法(双指针)
class Solution {
private:
int result=INT_MAX;
TreeNode* pre;
void traversal(TreeNode* root){
if(root==NULL) return;
traversal(root->left);
if(pre){
result=min(result,root->val-pre->val);
}
pre=root;
traversal(root->right);
}
public:
int getMinimumDifference(TreeNode* root) {
traversal(root);
return result;
}
};
方法二、迭代方法
class Solution {
public:
int getMinimumDifference(TreeNode* root) {
if(root==NULL) return 0;
stack<TreeNode*> st;
int result=INT_MAX;
TreeNode* pre=NULL;
TreeNode* cur=root;
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;
}
};
题目描述:
给定一个有相同值的二叉搜索树(BST),找出 BST 中的所有众数(出现频率最高的元素)。
假定 BST 有如下定义:
结点左子树中所含结点的值小于等于当前结点的值
结点右子树中所含结点的值大于等于当前结点的值
左子树和右子树都是二叉搜索树
例如:
返回[2].
提示:如果众数超过1个,不需考虑输出顺序
进阶:你可以不使用额外的空间吗?(假设由递归产生的隐式调用栈的开销不被计算在内)
思路核心:
如果 频率count 等于 maxCount(最大频率),当然要把这个元素加入到结果集中(以下代码为result数组)
当频率count 大于 maxCount的时候,不仅要更新maxCount,而且要清空结果集(以下代码为result数组),因为结果集之前的元素都失效了
方法一、递归法(中序遍历)
class Solution {
private:
int count;
int maxcount;
TreeNode* pre;
vector<int> result;
void traversal(TreeNode* cur){
if(cur==NULL) return;
traversal(cur->left);
if(pre==NULL) 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){
result.clear();
result.push_back(cur->val);
maxcount=count;
}
pre=cur;
traversal(cur->right);
return;
}
public:
vector<int> findMode(TreeNode* root) {
count=0;
maxcount=0;
pre=NULL;
traversal(root);
return result;
}
};
方法二、迭代法(中序遍历)
class Solution {
public:
vector<int> findMode(TreeNode* root) {
vector<int> result;
if(root==NULL) return result;
stack<TreeNode*> st;
int count=0;
int maxcount=0;
TreeNode* pre=NULL;
TreeNode* cur=root;
while(!st.empty() || cur){
if(cur){
st.push(cur);
cur=cur->left;
}
else{
cur=st.top();
st.pop();
if(pre==NULL) 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();
result.push_back(cur->val);
maxcount=count;
}
pre=cur;
cur=cur->right;
}
}
return result;
}
};
题目描述:
给定一个二叉树, 找到该树中两个指定节点的最近公共祖先。
百度百科中最近公共祖先的定义为:“对于有根树 T 的两个结点 p、q,最近公共祖先表示为一个结点 x,满足 x 是 p、q 的祖先且 x 的深度尽可能大(一个节点也可以是它自己的祖先)。”
输入: root = [3,5,1,6,2,0,8,null,null,7,4], p = 5, q = 1
输出: 3
解释: 节点 5 和节点 1 的最近公共祖先是节点 3。
输入: 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
说明:
所有节点的值都是唯一的
p、q 为不同节点且均存在于给定的二叉树中
遇到这个题目首先想的是要是能自底向上查找就好了,这样就可以找到公共祖先了。
那么二叉树如何可以自底向上查找呢?
回溯啊,二叉树回溯的过程就是从低到上。
后序遍历就是天然的回溯过程,最先处理的一定是叶子节点。
接下来就看如何判断一个节点是节点q和节点p的公共公共祖先呢。
「如果找到一个节点,发现左子树出现结点p,右子树出现节点q,或者 左子树出现结点q,右子树出现节点p,那么该节点就是节点p和q的最近公共祖先。」
使用后序遍历,回溯的过程,就是从低向上遍历节点,一旦发现如何这个条件的节点,就是最近公共节点了。
class Solution {
public:
TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
if(root==p || root==q || root==NULL) return root;
TreeNode* lefttree=lowestCommonAncestor(root->left,p,q);
TreeNode* righttree=lowestCommonAncestor(root->right,p,q);
if(lefttree && righttree) return root;
else if(lefttree && righttree==NULL) return lefttree;
else if(lefttree==NULL && righttree) return righttree;
else return NULL;
}
};
1.求最小公共祖先,需要从底向上遍历,那么二叉树,只能通过后序遍历(即:回溯)实现从低向上的遍历方式。
2.在回溯的过程中,必然要遍历整颗二叉树,即使已经找到结果了,依然要把其他节点遍历完,因为要使用递归函数的返回值(也就是代码中的left和right)做逻辑判断。
3.要理解如果返回值left为空,right不为空为什么要返回right,为什么可以用返回right传给上一层结果。
欢迎大家扫码关注本人公众号:编程复盘与思考随笔
(关注后可以免费获得本人在csdn发布的资源源码)
公众号主要记录编程和刷题时的总结复盘笔记和心得!并且分享读书、工作、生活中的一些思考感悟!
想要组队一起参加阿里天池,kaggle,百度飞浆,科大讯飞等AI相关的比赛的同学可以扫下面的二维码加微信一起讨论学习!