530.二叉搜索树的最小绝对差
题目链接:530.二叉搜索树的最小绝对差
难度指数:😀🙂
递归
直白想法:(暴力)
先进行中序遍历,把二叉搜索树转变成一个有序的数组,然后就找相邻的最小绝对差。
双指针优化:
在中序遍历时,利用2个指针直接得出最小绝对差是多少,(而无需把二叉搜索树转变成数组,这样多开辟了空间,太折腾。)
cur指向的数值减去pre指向的数值,这个差值就是相邻两个结点的差值,再用一个result记录这些差值中的最小值。
代码思路:
本题递归函数不需要有返回值 (建议重看视频)
int result = MAX_INT; //记录最小差值
TreeNode* pre = NULL;
void traversal(TreeNode* cur) { //cur表示当前遍历的结点,(这里参数没有写成root,有利于你理解cur和pre的先后顺序)
if (cur == NULL) {
return;
}
traversal(cur->left); //左
//中的处理逻辑:比较这两个节点的差值,让result记录最小的差值
if (pre != NULL) {
result = min(result, cur->val - pre->val); //min(之前result的最小值, cur的数值 - pre的数值)
}
pre = cur;
traversal(cur->right); //右
}
if (pre != NULL) { result = min(result, cur->val - pre->val); //min(之前result的最小值, cur的数值 - pre的数值) }
保证我们一直都是取最小的。
最后的 result 记录的就是遍历整个二叉搜索树之后的最小差值。
Q:关键:
pre如何紧跟着cur,指向cur的上一个节点?A:很简单,让
pre = cur;
遍历第一个节点的时候,是不需要进行两个节点的相减,
建议10:00重新看看
AC代码: (核心代码模式)
/**
* 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:
int result = INT_MAX;
TreeNode* pre = NULL;
void traversal(TreeNode* cur) {
if (cur == NULL) {
return;
}
traversal(cur->left); //左
//中
if (pre != NULL) {
result = min(result, cur->val - pre->val);
}
pre = cur;
traversal(cur->right); //右
}
public:
int getMinimumDifference(TreeNode* root) {
traversal(root);
return result;
}
};
迭代
代码思路:
501.二叉搜索树中的众数
题目链接:501.二叉搜索树中的众数
难度指数:😀😕
本题对二叉搜索树进行了重新定义,在这颗二叉搜索树中允许有重复的元素。
最终要求输出众数的集合,把所有的众数放到一个数组里。
递归
直白想法:(暴力)
当成是普通二叉树来处理
AC代码: (核心代码模式)
双指针优化:
利用二叉搜索树的特性:其中序遍历是有序的。
代码思路:
Q:如何在有序的二叉树中求众数?
A:给你一个有序的数组,让你求众数,so easy;但给你一棵有序的二叉树,让你求众数,懵了吧。难在遍历方式上面
求众数的具体方法:
Count是统计单个元素出现的频率,maxCount是所有这些元素中最高频率。
如果 cur 和 pre 指向的值相等,就统计 Count ,(当 cur 指向遍历的第一个元素,此时还没有 pre ,那么默认的 Count 就是1 )
Q:返回值为什么是
void?A:因为我们遍历的是整棵二叉树,而且不需要递归函数给我们返回什么,因为我们在遍历的过程中直接对元素进行统计,结果也全都放在全局变量里,所以不需要有返回值。
中是处理逻辑,需要统计这个元素出现的频率,即Count
TreeNode* pre = NULL;
int Count = 0; //统计单个元素出现的频率
int maxCount = 0; //统计整个二叉树中元素出现的最高频率 (至少是统计出现过的)
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) { //pre和cur指向的数值相等
Count++;
}
else { //pre和cur指向的数值不相等
Count = 1; //就又回到1
}
//让pre跟在cur后面
pre = cur;
if (Count == maxCount) {
result.push_back(cur->val);
}
if (Count > maxCount) { //有找到比当前maxCount更高频率的
maxCount = Count; //就该更新maxCount了
result.clear(); //清空result数组中的旧元素
result.push_back(cur->val);
}
//右
traversal(cur->right);
return;
}
if (Count == maxCount) { result.push_back(cur->val); }
写到这一步,有人可能会开始疑惑:maxCount好像还没有赋值啊,还是0,你直接就要if (Count == maxCount),就把这个当前数值放到result里面,那么这个结果集result数组里面是不是放的不是我们想要的结果。
别担心,后面还会有逻辑来更新这个result数组。
if (Count > maxCount) { //有找到比当前maxCount更高频率的 maxCount = Count; //就该更新maxCount了 }
接下来需要清空result数组,
因为maxCount更新了,说明前面那个 if语句 以旧的maxCount值的判断逻辑,把对应的数值放到result数组里,result数组里的元素不是我们真正的结果集想要的元素。(即旧的那些已经不是二叉搜索树中频率最高的元素,因为maxCount已经被更新了)
所以需要将之前result数组中放的结果都清空。
AC代码: (核心代码模式)
/**
* 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:
TreeNode* pre = NULL;
int Count = 0; //统计单个元素出现的频率
int maxCount = 0; //统计整个二叉树中元素出现的最高频率 (至少是统计出现过的)
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) { //pre和cur指向的数值相同
Count++;
}
else { //pre和cur指向的数值不相同
Count = 1; //开始统计cur指向的新的元素出现的频率
}
//让pre跟在cur的后面
pre = cur;
if (Count == maxCount) {
result.push_back(cur->val);
}
if (Count > maxCount) { //有找到比当前maxCount更高频率的
maxCount = Count; //更新maxCount
result.clear(); //记得清空result数组中的旧元素
result.push_back(cur->val);
}
traversal(cur->right); //右
return;
}
public:
vector<int> findMode(TreeNode* root) {
TreeNode* pre = NULL;
int Count = 0;
int maxCount = 0;
result.clear();
traversal(root);
return result;
}
};
总结:
……
你要说有涉及到什么算法吗?其实也没有。
这些就是写代码的一些悟性,或者说是代码功底,并没有涉及到什么算法,
但是这些代码的技巧确实能帮你减小代码的运行时间,
所以代码能力或者说工程能力,有时候不是非得学什么高深的算法,有时候就是考虑一些细节问题,就能让代码的运行效率提高。
236.二叉树的最近公共祖先
题目链接:236.二叉树的最近公共祖先
难度指数:😀😐😕
找到p和q,从底往上遍历,一旦交汇,那这个节点就是最近公共祖先。想法很直观,但是二叉树只能从根节点开始往下遍历。
其实,遍历二叉树是没法从下往上遍历,但是,处理顺序是可以从下往上处理的。
在二叉树中,有递归就会有回溯。回溯的过程,就会从底往上处理结果。
通过回溯就可以判断某个节点的左子树有没有出现过p,右子树有没有出现过q。
若有,将这个7向上返回。最后,p和q的公共祖先的值就传到根节点。
想在回溯中达到这种效果,一定要用后序遍历。
05:30
……
我们只需判断:左子树中只要出现了p或q,就往上返回;右子树只要出现p或q,就往上返回。
左不为空,右也不为空,那么这个中就一定是p和q的最近公共祖先。(这是第一种情况)
第二种情况:如果p是6,q是7,那么q的值本来就是p和q的公共祖先。
(在处理第一种情况的时候,就可以把第二种情况顺便处理了,即处理逻辑是可以重合的。)
同学疑惑:“这个判断条件可能有漏洞啊。” ?
若底下的这两个
红色节点都是6,这个判断逻辑也会判断这个7是公共祖先呀。
其实题目信息说得很清楚:二叉树每个数值都是不同的,并且二叉树中一定存在p和q。
代码思路:(后序)
递归函数的返回值是返回二叉树中p和q的公共祖先。
TreeNode* traversal(root, q, p) {
if (root == NULL) { //若传进来是空
return NULL; //则return也是空
}
//另一个终止条件
if (root == p || root == q) { //root在往下遍历的过程中,遇到p或q
return root; //就是将p或q返回
}
//单层递归的逻辑
TreeNode* left = traversal(root->left, q, p); //左
TreeNode* right = traversal(root->right, q, p); //右
//中
if (left != NULL && right != NULL) {
return root;
}
if (left == NULL && right != NULL) {
return right;
}
else if (left != NULL && right == NULL) {
return left;
}
else {
return NULL;
}
}
题目思路还是有点难的,要注意一些细节。
若 root 在往下遍历的过程中,如果遇见了 p ,要把 p 往上返回,告诉上一层节点:这里遇到了 p,
left 告诉我们:左子树中有没有出现过 p 和 q 。
这道题理解得并不深刻,二刷的时候重点关注
AC代码: (核心代码模式)
/**
* 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 == NULL) {
return NULL;
}
//另一个终止条件
if (root == p || root == q) { //root在往下遍历的过程中,遇到p或q
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;
}
else if (left != NULL && right == NULL) {
return left;
}
else {
return NULL;
}
}
};