代码随想录训练营18day-二叉树7

一、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;
}

  • 23
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值