530.二叉搜索树的最小绝对差
题目链接/文章讲解: 代码随想录视频讲解:二叉搜索树中,需要掌握如何双指针遍历!| LeetCode:530.二叉搜索树的最小绝对差_哔哩哔哩_bilibili
1.方法1
1.1分析及思路
了解到差值最小的数为,结点左孩子然后一直向右的叶子结点,或者是右孩子一直向左的叶子结点。
对每一个结点都进行这样的判断。所以就用到了递归。
递归三部曲:
1.功能,参数,返回值
功能:判断结点的最小差值
参数:保存最小值的int*地址,以及结点
返回值:没有必要向上层返回值,所以不需要返回值
2.递归终止条件
假设传入一个非空结点,要进行的操作就是,寻找其前后最接近的值。所以只有NULL是才会结束循环。
3.单层递归逻辑
拿到一个结点,要进行寻找其左孩子的最右结点,以及右孩子的最左结点,然后求最小差值。
1.2代码及注释
void LETF(struct TreeNode* root,int* Min) {// 定义一个指向左子树的节点Nodestruct TreeNode* Node = root->left;// 遍历左子树while(Node!=NULL){// 如果Node的右子节点为空,则说明找到了,左孩子的最右侧的叶子结点if(Node->right == NULL)// 更新最小值Minif(*Min > root->val - Node->val)*Min = root->val - Node->val;Node = Node->right;}
}void RIGHT(struct TreeNode* root,int* Min) {// 定义一个指向右子树的节点Nodestruct TreeNode* Node = root->right;// 遍历右子树while(Node!=NULL){// 如果Node的左子节点为空,则说明找到了右孩子的最左侧叶子结点if(Node->left == NULL)// 更新最小值Minif(*Min > Node->val - root->val)*Min = Node->val - root->val;Node = Node->left;}
}void traversal(struct TreeNode* root,int* Min) {// 如果当前节点为空,直接返回if(root == NULL) return;if(root->left != NULL)LETF(root,Min);//寻找左孩子的最右侧叶子结点if(root->right != NULL)RIGHT(root,Min);//寻找右孩子的最左侧结点// 递归遍历左子树traversal(root->left,Min);// 递归遍历右子树traversal(root->right,Min);
}int getMinimumDifference(struct TreeNode* root) {// 初始化最小值为INT_MAXint Min = INT_MAX;// 从根节点开始遍历traversal(root,&Min);// 返回最小值return Min;
}
2.方法2
2.1分析及思路
与98.验证二叉搜索树思路类似,用两个指针进行遍历,这俩个指针的差值是可能成为最小差值的,因为中序遍历是会得到一个递增的数组的。
1.功能、参数、返回值
功能:得到最小的差值
参数:保存最小差值的Min、根节点root、访问过的前一个结点pre
返回值:下一侧递归不需要及时返回信息,所以不需要返回值
2.终止条件
当访问到NULL时说明该树已经访问完毕。所以终止条件为NULL
3.单层递归的逻辑
拿到一个结点root,用root-pre判断差值,然后递归的访问其左子树,右子树
2.2代码及分析
// 定义一个函数用于遍历二叉搜索树
void traversal(struct TreeNode* root, struct TreeNode* *pre, int* Min) {// 如果当前节点为空,直接返回if(root == NULL) return;// 递归遍历左子树traversal(root->left, pre, Min);// 如果前一个节点不为空if(*pre != NULL){// 更新最小差值if(*Min > (root->val - (*pre)->val)){*Min = (root->val - (*pre)->val);}}// 更新前一个节点为当前节点*pre = root;// 递归遍历右子树traversal(root->right, pre, Min);
}// 定义一个函数用于获取二叉搜索树中节点值的最小差值
int getMinimumDifference(struct TreeNode* root) {// 初始化最小差值为最大整数值int Min = INT_MAX;// 初始化前一个节点为空struct TreeNode* pre = NULL;// 调用遍历函数traversal(root, &pre, &Min);// 返回最小差值return Min;
}
501.二叉搜索树中的众数
代码随想录
视频讲解:不仅双指针,还有代码技巧可以惊艳到你! | LeetCode:501.二叉搜索树中的众数_哔哩哔哩_bilibili
1.分析及思路
最直观的想法就是,访问一遍树,把所有的数出现的频率都统计下来,然后再把频率高的数进行返回。
进行优化,对于二叉搜索树而言,中序遍历会得到一个不减的数组,和上一题类似,使用双指针进行频率的统计,如果当前频率等于最大频率,则说明众数有多个,在结果数组的末尾加上它即可。若大于则说明,已经统计的结果全部作废,要重新统计,即再次从数组下标为0的地方存储结果。
运用了中序遍历,所以可以使用递归法,递归三部曲:
1.功能,参数,返回值
功能:统计二叉树中的众数
参数:需要传入一个根节点,所以有struct TreeNode* root
因为是双指针,所以需要一个struct TreeNode** pre,保存上次访问过的结点
最终返回的是一个数组,所以穿进来一个int* result
以及统计数组的大小的int* returnSize
需要当前频率和最大频率做对比,所以有int* MaxCount,int* NowCount。
2.递归终止条件
很明显,遇到空结点时,没办法统计所以终止。
3.单层递归逻辑
遇到一个结点,看上次访问的结点与它是否相等,相等则频率加1。若不想等则说明,此时访问的是新出现的,此时频率设置为1。若该频率与最大频率相等,则把它加入结果数组的末尾。若大于,则说明,结果数组中的众数全部作废,重新统计。
2.代码及注释
void traversal(struct TreeNode* root, struct TreeNode** pre, int* returnSize, int* result, int* MaxCount, int* NowCount) {// 如果当前节点为空,直接返回if(root == NULL) return;// 递归遍历左子树traversal(root->left, pre, returnSize, result, MaxCount, NowCount);// 如果前一个节点为空,则当前节点是第一个节点,NowCount置为1if(*pre == NULL) *NowCount = 1;// 如果前一个节点的值等于当前节点的值,则当前节点是重复节点,NowCount加1else if((*pre)->val == root->val) (*NowCount)++;// 如果前一个节点的值不等于当前节点的值,则当前节点是新节点,NowCount置为1else *NowCount = 1;// 更新pre指针为当前节点*pre = root;// 如果NowCount等于MaxCount,将当前节点的值加入结果数组并增加返回大小if(*NowCount == *MaxCount) result[(*returnSize)++] = root->val;// 如果NowCount大于MaxCount,更新MaxCount为NowCount,重置返回大小为1,将当前节点的值放入结果数组else if(*NowCount > *MaxCount){*MaxCount = *NowCount;*returnSize = 1;result[0] = root->val;}// 递归遍历右子树traversal(root->right, pre, returnSize, result, MaxCount, NowCount);
}int* findMode(struct TreeNode* root, int* returnSize) {// 初始化返回大小为0*returnSize = 0;// 分配结果数组的内存空间int* result = (int*)malloc(sizeof(int)*10001);// 初始化前一个节点指针、最大出现次数、当前出现次数为0struct TreeNode* pre = NULL;int MaxCount = 0;int NowCount = 0;// 调用traversal函数进行遍历traversal(root, &pre, returnSize, result, &MaxCount, &NowCount);// 返回结果数组return result;
}
236. 二叉树的最近公共祖先
代码随想录
视频讲解:自底向上查找,有点难度! | LeetCode:236. 二叉树的最近公共祖先_哔哩哔哩_bilibili
1.分析及思路
很明显,要想知道最近公共祖先结点,就要从该结点左右子树中找到qp两个结点。也就是先访问其左子树,再访问其右子树,也就是使用的是后续遍历。即找到结点后就向父结点返回。
递归三部曲:
1.功能、参数、返回值
功能:返回最近公共祖先
参数:根节点、p、q
返回值:因为需要向父结点返回有没有该结点,所以需要返回值
2.递归终止条件
当传入NULL时,很明显终止。当找到该结点时,也应该终止。
3.单层递归逻辑
遇到结点时,先访问其左子树,在访问其右子树,才能得到该结点是不是有,目标结点。
1.若左子树有目标结点,右子树也有目标结点,则说明它就是最近公共祖先。
2.若右子树有,左子树没有,则说明最近公共祖先在右侧,即返回右侧返回的结点
3.左子树有,右子树没有,则返回左子树返回的值
4.若都没有很明显,返回NULL
2.代码及注释
// 定义一个函数,用于找到两个节点的最近公共祖先
struct TreeNode* lowestCommonAncestor(struct TreeNode* root, struct TreeNode* p, struct TreeNode* q) {// 如果根节点为空,返回空if(root == NULL) return root;// 如果根节点等于其中一个节点,直接返回根节点if(root == q || root == p) return root;// 递归查找左子树中的最近公共祖先struct TreeNode* left = lowestCommonAncestor(root->left,p,q);// 递归查找右子树中的最近公共祖先struct 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;// 如果左右子树都没有找到最近公共祖先,则返回空return NULL;
}