一、530.二叉搜索树的最小绝对差
利用二叉搜索树的有序性,每一层遍历时候,最小差一定是在相邻的两个节点间产生的,因此做递归的时候,记录一个pre和cur节点,用来比较差值,迭代更新时候,记录最小值。二叉搜索树中序遍历,能有效利用有序性。
int result;
struct TreeNode* pre;
void trans(struct TreeNode* root)
{
if(root == NULL)
{
return;
}
/**左子节点**/
trans(root->left);
//取值判断
if(pre)
{
result = (root->val - pre->val) < result? root->val - pre->val : result;
}
pre = root;
//右子节点
trans(root->right);
}
int getMinimumDifference(struct TreeNode* root) {
pre = NULL;
result = INT_MAX;
trans(root);
return result;
}
迭代法:根据之前的中序遍历,进行修改:这里也需要记录pre和cur节点,方便进行比较最小差值。
int getMinimumDifference(struct TreeNode* root) {
// pre = NULL;
// result = INT_MAX;
// trans(root);
// return result;
//迭代法
struct TreeNode** nodeStk = (struct TreeNode**)malloc(sizeof(struct TreeNode*) * 10001);
int top = -1;
int result = INT_MAX;
struct TreeNode* cur = root;
struct TreeNode* pre = NULL;
while(cur || top > -1)
{
if(cur)//当前左边一直有节点
{
nodeStk[++top] = cur;
cur = cur->left;
}
else
{
cur = nodeStk[top];//遇到空节点了,pop出来叶子节点
top--;
if(pre)
{
result = (cur->val - pre->val) < result? cur->val - pre->val : result;
}
pre = cur;//更新上一个节点
cur = cur->right;//更新cur节点
}
}
return result;
}
二、501.二叉搜索树中的众数
本题的二叉搜索树:
- 结点左子树中所含结点的值小于等于当前结点的值
- 结点右子树中所含结点的值大于等于当前结点的值
- 左子树和右子树都是二叉搜索树
众数可能不止一个,这点就需要注意,比如二叉搜索树众数为3 4,他们的节点个数都是5个,那么这两个值都会作为返回值。但此时如果再遍历,发现另一个数的次数为6,那么这个数就是最大的众数,前面保存的众数的数字会被清空,这一点需要注意!!!!
回退条件:1 pre为空 说明是根节点,次数(众数)为1;
2 如果pre的值是和cur的值一样,那么这个数的次数+1;
3 其他情况 次数为1;
递归: 先遍历左子树,
记录pre = cur
如果maxcount == count
如果 count > maxcount,需要清空之前的结果;
int maxcount;
int count;
int size;
struct TreeNode* pre;
int* result;
void trans(struct TreeNode* cur)
{
if(cur == NULL)
{
return;
}
trans(cur->left);//遍历左子树
if(pre == NULL)
{
count = 1;//说明到根节点 还没更新
}
else if(pre->val == cur->val)
{
count++;
}
else
{
count = 1;
}
pre = cur;
if(maxcount == count)
{
//count = maxcount;
result[size++] = cur->val;
}
if(count > maxcount)
{
//清空result
maxcount = count;
size = 0;
result[size++] = cur->val;
}
trans(cur->right);
}
int* findMode(struct TreeNode* root, int* returnSize) {
maxcount = 0;
count = 0;
size = 0;
pre = NULL;
result = (int*)malloc(sizeof(int) * 10001);
trans(root);
*returnSize = size;
return result;
}
迭代方式,亦用栈的方式保存,类似中序遍历写法:
int* findMode(struct TreeNode* root, int* returnSize) {
// maxcount = 0;
// count = 0;
// size = 0;
// pre = NULL;
// result = (int*)malloc(sizeof(int) * 10001);
// trans(root);
// *returnSize = size;
// return result;
//迭代法
int* result = (int*)malloc(sizeof(int) * 10001);
int maxcount = 0;
int count = 0;
*returnSize = 0;
struct TreeNode* pre = NULL;
struct TreeNode* cur = root;
struct TreeNode** nodeStk = (struct TreeNode**)malloc(sizeof(struct TreeNode*) * 10001);
int top = -1;
while(cur || top > -1)
{
if(cur)
{
nodeStk[++top] = cur;
cur = cur->left;
}
else
{
cur = nodeStk[top];
top--;
if(pre == NULL)
{
count = 1;//说明到根节点 还没更新
}
else if(pre->val == cur->val)
{
count++;
}
else
{
count = 1;
}
pre = cur;
if(count == maxcount)
{
result[*returnSize] = cur->val;
(*returnSize)++;
}
if(count > maxcount)
{
(*returnSize) = 0;//清空
result[(*returnSize)++] = cur->val;
maxcount = count;
}
cur = cur->right;
}
}
return result;
}
三、236 二叉树的最近公共祖先
后序遍历可以回溯,递归函数返回值,可以作为判断:
1 递归的回退条件:root为空,即遇到空节点;root为p或者q,说明root递归到题目指定p或者q节点了,此时应该返回当前节点。
2 递归调用
3 判断祖先节点,如果递归回来的返回值都存在,那么root就是两个的公共祖先;
如果left为空,right不为空,那么返回right;
这里需要这样理解:二叉树节点数值是不重复的,而且一定存在 q 和 p。
但是很多人容易忽略一个情况,就是节点本身p(q),它拥有一个子孙节点q(p)
struct TreeNode* lowestCommonAncestor(struct TreeNode* root, struct TreeNode* p, struct TreeNode* q) {
if(!root || root == p || root == q )
{
return root;
}
struct TreeNode* left = lowestCommonAncestor(root->left, p, q);
struct 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 NULL;
}
}
四、701.二叉搜索树中的插入操作
插入操作需要根据插入值,去递归遍历,找到指定位置进行插入。
通过中序遍历,如果root为空,说明递归到空节点了,如果当前val大于当前节点值,那么插入到右边,否则左边。
struct TreeNode* insertIntoBST(struct TreeNode* root, int val) {
if(root == NULL)
{
struct TreeNode* newNode = (struct TreeNode*)malloc(sizeof(struct TreeNode));
newNode->val = val;
newNode->left = NULL;
newNode->right = NULL;
return newNode;
}
struct TreeNode* left, right;
if(root->val > val)
{
root->left = insertIntoBST(root->left, val);//把新创建的node给当前node的left
}
if(root->val < val)
{
root->right = insertIntoBST(root->right, val);
}
return root;
}
通过一个parent和cur,记录之前的节点作为父节点,当遍历到可以插入的位置时候,进行赋值。
struct TreeNode* insertIntoBST(struct TreeNode* root, int val) {
//迭代
if(root == NULL)
{
struct TreeNode* newNode = (struct TreeNode*)malloc(sizeof(struct TreeNode));
newNode->val = val;
newNode->left = NULL;
newNode->right = NULL;
return newNode;
}
struct TreeNode* parent = root;
struct TreeNode* cur = root;
while(cur)
{
parent = cur;
if(cur->val > val)
{
cur = cur->left;
}
else
{
cur = cur->right;
}
}
struct TreeNode* newNode = (struct TreeNode*)malloc(sizeof(struct TreeNode));
newNode->val = val;
newNode->left = NULL;
newNode->right = NULL;
if(parent->val > val)
{
parent->left = newNode;
}
else
{
parent->right = newNode;
}
return root;
}
五、450.删除二叉搜索树中的节点
删除节点需要考虑节点的位置,如果找到了key对应的节点
1 当前节点无后继节点,即它是叶子节点,直接删除;
2 如果当前节点无左子树,那么当前节点的右节点作为返回值;
3 如果当前节点无右子树,那么当前节点的左节点作为返回值;
4 就是左右子树都存在的情况,这个复杂一些:
1)把当前节点的左右子节点保存;
2)遍历当前节点右节点所在的右子树,找到最左边节点left;
3)把当前节点的左节点,挂到left的左孩子位置(见code)
struct TreeNode* deleteNode(struct TreeNode* root, int key){
/**情况1 root为空**/
if(root == NULL)
{
return NULL;
}
if(root->val == key)
{
//情况2 无子树
if(!root->left && !root->right)
{
//直接删除root,返回NULL
free(root);
return NULL;
}
else if(!root->left)
{
//情况3 left不存在 删除root,right作为新根结点
struct TreeNode* tmp = root->right;
free(root);
return tmp;
}
else if(!root->right)
{
//情况4 left不存在 删除root,right作为新根结点
struct TreeNode* tmp = root->left;
free(root);
return tmp;
}
else
{
//情况5
//头结点的左右子树都存在
//找到当前删除节点的右子树的最左边节点
struct TreeNode* cur_left = root->left;
struct TreeNode* cur_right = root->right;
struct TreeNode* cur = root->right;//当前节点右子节点
while(cur->left)
{
cur = cur->left;
}
//删除节点的右子树的最左节点->left = cur
cur->left = cur_left;
//删除root(当前节点)
free(root);
return cur_right;
}
}
if(key < root->val)
root->left = deleteNode(root->left, key);
if(key > root->val)
root->right = deleteNode(root->right, key);
return root;
}
迭代方式:把左右子节点都存在的情况封装成一个函数;
1 首先找到等于key的节点和它的父节点;
2 如果pre父节点为空,说明是根节点,直接返回;
3 因为只是知道pre是父节点,但是cur是左还是或者还是右孩子,是不清楚的,因此需要判断,把cur节点记录,pre的节点处理,就和递归时候的左右孩子都在的情况一样进行处理。
struct TreeNode* deleteOneNode(struct TreeNode* cur)
{
if(cur == NULL) return cur;
if(!cur->right) return cur->left;
//if(!cur->left) return cur->right;
struct TreeNode* cur_left = cur->left;
struct TreeNode* cur_right = cur->right;
struct TreeNode* curNode = cur->right;//当前节点右子节点
while(curNode->left)
{
curNode = curNode->left;
}
//删除节点的右子树的最左节点->left = cur
curNode->left = cur_left;
//删除root(当前节点)
free(cur);
return cur_right;
}
struct TreeNode* deleteNode(struct TreeNode* root, int key){
//迭代法
if(root == NULL) return root;
struct TreeNode* pre = NULL;
struct TreeNode* cur = root;
while(cur)
{
if(cur->val == key)
{
break;
}
pre = cur;
if(cur->val > key)
{
cur = cur->left;
}
else
{
cur = cur->right;
}
}
if(pre == NULL)
{
//只有头结点
return deleteOneNode(cur);
}
//pre是当前的节点作为左或者右孩子的父节点
if(pre->left && pre->left->val == key)
{
pre->left = deleteOneNode(cur);
}
if(pre->right && pre->right->val == key)
{
pre->right = deleteOneNode(cur);
}
return root;
}
六、669. 修剪二叉搜索树
这里需要注意一个点,利用返回值来移除节点。
1 首先找不符合的节点,当前节点值小于low,那么去右边子树找(右比左大),返回符合条件的节点;
2 递归左子树,返回的是左子树上符合条件的节点,当前节点左节点就是它。
struct TreeNode* trimBST(struct TreeNode* root, int low, int high) {
//递归
if(!root) return NULL;
if(root->val < low) return trimBST(root->right, low, high);
if(root->val > high) return trimBST(root->left, low, high);
root->left = trimBST(root->left, low, high);
root->right = trimBST(root->right, low, high);
return root;
}
迭代法:1 节点移动到[low, high]区间;
2 如果当前节点的左节点小于low,说明是不符合条件的节点,需要删除此节点,将当前节点left用当前left右节点替代,并一直遍历下去,因为需要保证右节点所在的树,里面也没有不满足范围的值;
struct TreeNode* trimBST(struct TreeNode* root, int low, int high) {
//递归
// if(!root) return NULL;
// if(root->val < low) return trimBST(root->right, low, high);
// if(root->val > high) return trimBST(root->left, low, high);
// root->left = trimBST(root->left, low, high);
// root->right = trimBST(root->right, low, high);
// return root;
//迭代法:
if(!root) return NULL;
//遍历到范围内的节点
while(root &&(root->val < low || root->val > high))
{
if(root->val < low)
{
root = root->right;//if值小于low,往右走
}
else
{
root = root->left;
}
}
struct TreeNode* cur = root;
//删除节点
while(cur)
{
while(cur->left &&(cur->left->val < low))
{
cur->left = cur->left->right;
}
cur = cur->left;
}
cur = root;
while(cur)
{
while(cur->right &&(cur->right->val > high))
{
cur->right = cur->right->left;
}
cur = cur->right;
}
return root;
}
七、108.将有序数组转换为二叉搜索树
这个题目需要注意区间的封闭原则,保证在处理过程中左闭右闭;
1 如果左闭右闭,那么left和right都能被取到,因此left 大于right才会退出;
2 递归时候,mid -1能被取到,mid + 1能被取到
struct TreeNode* trans(int* nums, int left, int right)
{
/**左闭右闭原则**/
if(left > right)
return NULL;
int mid = left + (right - left)/2;
struct TreeNode* root = (struct TreeNode*)malloc(sizeof(struct TreeNode));
root->val = nums[mid];
root->left = NULL;
root->right = NULL;
root->left = trans(nums, left, mid - 1);
root->right = trans(nums, mid + 1, right);
return root;
}
struct TreeNode* sortedArrayToBST(int* nums, int numsSize) {
if(numsSize == 0)
{
return NULL;
}
return trans(nums, 0, numsSize - 1);//左闭右闭
}
迭代方式稍微复杂,需要使用3个数组,保存节点和左右边界。
八、把二叉搜索树转换为累加树
使用反中序遍历,需要记录pre和cur节点,这里可以只是结论pre的value,因为只是改变节点的值,首先遍历右节点,拿到当前节点的值,加上pre的值,更新当前值;然后pre的值更新成当前的value,这样每一次拿到节点后,pre上面存到的都是之前的累加值。
满足:1 更新当前值;
2 累加值加入到当前值;
int pre;
void trans(struct TreeNode* cur)
{
if(!cur)
return ;
trans(cur->right);
cur->val += pre;//把上一个节点的值加到当前节点
pre = cur->val;//更新当前节点值到pre
trans(cur->left);
}
struct TreeNode* convertBST(struct TreeNode* root) {
pre = 0;//记录前一个节点的值
if(root == NULL)
{
return NULL;
}
trans(root);
return root;
}
利用遍历写法,更新pre和cur的value。
struct TreeNode* convertBST(struct TreeNode* root) {
//pre = 0;//记录前一个节点的值
if(root == NULL)
{
return NULL;
}
// trans(root);
// return root;
//迭代法
struct TreeNode** stk = (struct TreeNode**)malloc(sizeof(struct TreeNode*) * 10001);
int top = -1;
struct TreeNode* cur = root;
//struct TreeNode* pre = NULL;
int pre = 0;
while(cur || top > -1)
{
if(cur)
{
stk[++top] = cur;//入栈
cur = cur->right;
}
else
{
cur = stk[top];
top--;//出栈
cur->val +=pre;
pre = cur->val;
cur = cur->left;
}
}
return root;
}