算法第17天|继续二叉树:二叉搜索树的最近公共祖先、二叉搜索树中的插入操作、删除二叉搜索树中的节点

今日总结

        1、删除二叉搜索树中的节点(需要着重复习)

                当一个二叉树题目中用到返回值时,一定要清楚返回值是什么?返回的东西是赋值给什么变量的,什么时候添加返回值,什么时候接收返回值。
       

        2、遇到二叉搜索树要思考的问题:

                当遇到二叉搜索树,需要明白递归的方式是从上到下,可以根据值的大小找到对应的递归路径(属于递归三部曲中的确定单层递归逻辑)

        3、二叉搜索树中的插入操作

                要理解二叉搜索树的插入操作其实是找到合适的一个节点的空子节点(根据二叉树的性质进行递归选择路径直到找到空子节点,就插入),不需要对二叉树进行复杂的操作

        4、寻找二叉搜索树的最近公共祖先、二叉树的最近公共祖先(两种题型)

                要理解二叉搜索树为什么找到最近公共祖先简单;

                要清楚如何去找到二叉树的最近公共祖先:

                        (1)最近公共祖先有两种形式

                        (2)递归的输入返回值是什么?

                        (3)停止条件为什么可以找到一个p、q就可以返回的原因?

                        (4)递归的单层逻辑

二叉搜索树的最近公共祖先

题目链接:235. 二叉搜索树的最近公共祖先 - 力扣(LeetCode)

整体思路:

        思路1:利用二叉树的最近公共祖先做题

                p、q可能在不同的树上-->有一个最近的公共祖先

                p、q可能有一个本身就是最近公共祖先

                1、确定递归的输入、返回

                        输入:当前节点、p、q

                       返回:是否有p、q,有就返回对应的节点,没有就返回nullptr

                2、确定返回、停止条件

                        如果是空节点就返回nullptr

                        如果当前节点是p或者q就返回节点

                3、确定单层递归逻辑

                        使用后续遍历,获取左右子树返回值,判断当前元素返回什么。

        思路2:利用二叉搜索树的性质做题

                二叉搜索树的左子树全部小于当前节点,右子树全部大于当前节点:可以从上往下遍历,确定具体的遍历路径

                1、从上往下遍历,使用前序遍历方法,确定递归的输入、返回

                        需要确定pq的大小,p是小值,q是大值

        if(p->val>q->val)
        swap(p,q);
        return digui(root,p,q);

                        输入值:当前节点、p、q

                        返回值:目标节点:

    //根据二叉搜索树确定递归路线
    //返回值:返回一个节点(p,q,找到的最近公共祖先)
    //输入值:三个节点,pq排序
    TreeNode* digui(TreeNode* cur, TreeNode* p, TreeNode* q)
                2、确定递归的停止、返回逻辑

                        如果当前节点是空节点,就返回nullptr

if(cur==nullptr)return nullptr;
                3、确定递归单层逻辑

                        规划递归路线:当前值比两个目标值都大-->左子树;当前值比目标值都小-->右子树。

                        需要明确:返回值返回的是p、q、最近公共祖先

                        当前节点在pq范围中,说明本身就是最近公共祖先,返回自己

                        当前节点小于p、q,需要递归左子树

                        当前节点大于p、q,需要递归右子树

                        当前节点==p或者q,需要返回自己

        //按照二叉搜索树的性质去找到对应的最近公共祖先
        if(cur->val>p->val&&cur->valval)return cur;//找到了
        else if(cur->valval&&cur->valval)
        {
            return digui(cur->right,p,q);
        }
        else if(cur->val>p->val&&cur->val>q->val)
        {
            return digui(cur->left,p,q);
        }
        return cur;
    }

整体代码:

/**
 * 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:

    //根据二叉搜索树确定递归路线
    //返回值:返回一个节点(p,q,找到的最近公共祖先)
    //输入值:三个节点,pq排序
    TreeNode* digui(TreeNode* cur, TreeNode* p, TreeNode* q)
    {
        if(cur==nullptr)return nullptr;
        //按照二叉搜索树的性质去找到对应的最近公共祖先
        if(cur->val>p->val&&cur->valval)return cur;//找到了
        else if(cur->valval&&cur->valval)
        {
            return digui(cur->right,p,q);
        }
        else if(cur->val>p->val&&cur->val>q->val)
        {
            return digui(cur->left,p,q);
        }
        return cur;
    }
    TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
        if(p->val>q->val)
        swap(p,q);
        return digui(root,p,q);
        
    }
};

二叉搜索树中的插入操作

题目链接:701. 二叉搜索树中的插入操作 - 力扣(LeetCode)

整体思路:

        思路1:二叉搜索树中序遍历变数组+插入+数组遍历变二叉搜索树

            1、二叉搜索树中序遍历变成有序数组

    vectorres;
    void digui(TreeNode* cur)
    {
        if(cur==nullptr)return ;
        digui(cur->left);
        res.push_back(cur->val);
        digui(cur->right);
    }

        2、有序数组中插入目标值

    TreeNode* insertIntoBST(TreeNode* root, int val) {
        digui(root);
        res.resize(res.size()+1);
        bool insert =false;
        for(int i=res.size()-2,j=res.size()-1;i>=0;i--)
        {
            if(res[i]>val)
            {
                res[j--] = res[i];
            }
            else
            {
                res[j--]= val;
                insert=true;
                break;
            }
        }
        if(insert==false)
            res[0]=val;

        3、将数组变成二叉搜索树(形状可能不同)

    //递归,去构造新的二叉搜索树
    //返回的是节点,输入的是数组
        TreeNode* fanhui(vector&res)
        {
            //停止条件:数组为空
            if(res.size()==0)return nullptr;
            //单层递归逻辑:
            //找到中间节点作为当前节点
            int mid= res.size()/2;
            TreeNode* cur  = new TreeNode(res[mid]);
            vector vec1(res.begin(),res.begin()+mid);
            cur->left = fanhui(vec1);
            vector vec2(res.begin()+mid+1,res.end());
            cur->right =fanhui(vec2);
            return cur;
            
        }

        思路1整体代码:

/**
 * 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 {
public:

    //思路1,中序遍历变成数组,将目标值加入,重新构造一棵树
    vectorres;
    void digui(TreeNode* cur)
    {
        if(cur==nullptr)return ;
        digui(cur->left);
        res.push_back(cur->val);
        digui(cur->right);
    }

    //递归,去构造新的二叉搜索树
    //返回的是节点,输入的是数组
        TreeNode* fanhui(vector&res)
        {
            //停止条件:数组为空
            if(res.size()==0)return nullptr;
            //单层递归逻辑:
            //找到中间节点作为当前节点
            int mid= res.size()/2;
            TreeNode* cur  = new TreeNode(res[mid]);
            vector vec1(res.begin(),res.begin()+mid);
            cur->left = fanhui(vec1);
            vector vec2(res.begin()+mid+1,res.end());
            cur->right =fanhui(vec2);
            return cur;
            
        }
    TreeNode* insertIntoBST(TreeNode* root, int val) {
        digui(root);
        res.resize(res.size()+1);
        bool insert =false;
        for(int i=res.size()-2,j=res.size()-1;i>=0;i--)
        {
            if(res[i]>val)
            {
                res[j--] = res[i];
            }
            else
            {
                res[j--]= val;
                insert=true;
                break;
            }
        }
        if(insert==false)
            res[0]=val;
        return fanhui(res);
        
    }
};

        思路二:通过二叉搜索树的性质直接插入

        关键点:插入是创造一个新的节点,通过二叉搜索树的性质,将其插入到一个节点的子节点处(空节点)

                        需要额外判断root==nullptr的情况

                1、递归的输入返回

                        输入:当前节点、目标值

                        返回:可以无返回值

    //通过二叉搜索树的性质从上往下遍历,去找到要添加的节点的位置
    //插入的位置一定是前一个节点的下一个节点是空节点的时候,可以有返回值,也可以不用返回值(需要记录前一个节点)
    //返回值:无需返回
    //输入值:当前节点、目标值
    TreeNode* cur =new TreeNode(0);
    void digui(TreeNode* root, int val)
                2、递归停止条件

                        当遍历到空节点时插入

        //停止条件:如果root==nullptr说明找到了通过前一个节点插入了
        if(root==nullptr)
        {
            TreeNode* newpoint = new TreeNode(val);
            if(cur->val>val)cur->left = newpoint;
            else cur->right = newpoint;
            root = newpoint;
            return ;
        }
                3、递归单层逻辑

                        (1)将当前的节点赋值给全局节点(记录前一个节点)

                        (2)通过二叉搜索树的性质进行递归

        cur =root;
        if(root->val>val)
        {
            digui(root->left,val);
        }
        else if(root->valright,val);
        }

        思路2整体代码

/**
 * 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 {
public:

    //通过二叉搜索树的性质从上往下遍历,去找到要添加的节点的位置
    //插入的位置一定是前一个节点的下一个节点是空节点的时候,可以有返回值,也可以不用返回值(需要记录前一个节点)
    //返回值:无需返回
    //输入值:当前节点、目标值
    TreeNode* cur =new TreeNode(0);
    void digui(TreeNode* root, int val)
    {
        //停止条件:如果root==nullptr说明找到了通过前一个节点插入了
        if(root==nullptr)
        {
            TreeNode* newpoint = new TreeNode(val);
            if(cur->val>val)cur->left = newpoint;
            else cur->right = newpoint;
            root = newpoint;
            return ;
        }
        cur =root;
        if(root->val>val)
        {
            digui(root->left,val);
        }
        else if(root->valright,val);
        }

    }
    TreeNode* insertIntoBST(TreeNode* root, int val) {
        if(root==nullptr)
        {
            TreeNode* newpoint = new TreeNode(val);
            return newpoint;
        }
        digui(root,val);
        return root;
    }
};

删除二叉搜索树中的节点

题目链接:450. 删除二叉搜索树中的节点 - 力扣(LeetCode)

整体思路:

        同样是通过二叉树的性质找到对应的节点,分情况讨论删除之后的效果

        1、确定递归的返回值、输入值

                输入值:当前节点、目标值

                返回值:我需要找到要删除的节点,之后将节点删除,将其子树返回,可以说是更新了当前的节点,所以返回值是当前更新的节点(如果没有更新就返回当前节点)

    //递归
    //返回值、输入值
    //返回值:当前的新节点
    //输入值:当前节点、目标值
    TreeNode* digui(TreeNode* cur,int val)

        2、确定递归的停止条件:(分情况讨论)

                (1)没找到要删除的点-->遍历到空节点-->返回nullptr

                (2)找到删除节点-->删除节点是叶子节点-->返回nullptr

                (3)找到删除节点-->删除节点子节点有一个不是空节点-->返回不是空节点的子节点

                (4)找到删除节点-->删除节点子节点都不是空节点-->返回右子节点,将左子节点放到右子节点的最左下的位置

        //停止条件:
        //1、遍历到空节点
        if(cur==nullptr)return nullptr;
        //2、找到删除节点,返回值有多种讨论
        if(cur->val==val)
        {
            //(1)当前节点是叶子节点,直接删除
            if(cur->left==nullptr&&cur->right==nullptr)return nullptr;
            //(2)当前节点有一个子节点是空节点,另一个子节点不是空节点,将不是空节点的子节点放到当前位置返回
            else if(cur->left==nullptr&&cur->right!=nullptr)return cur->right;
            else if(cur->left!=nullptr&&cur->right==nullptr)return cur->left;
            //(3)当前节点两个子节点都不是空节点(有两种方式,这里只将大的放到当前位置,小子节点放到大子节点的左下位置)
            else if(cur->left!=nullptr&&cur->right!=nullptr)
            {
                TreeNode* cur2=cur->right;
                while(cur2->left!=nullptr)
                {
                    cur2=cur2->left;
                }
                cur2->left = cur->left;
                return cur = cur->right;
            }
        }

        3、确定单层递归逻辑:

                如果当前值>目标值,递归左子树,将返回值赋给当前值的左子树

                如果当前值<目标值,递归右子树,将返回值赋给当前值的右子树

                返回当前值

整体代码:

/**
 * 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 {
public:
    //递归
    //返回值、输入值
    //返回值:当前的新节点
    //输入值:当前节点、目标值
    TreeNode* digui(TreeNode* cur,int val)
    {
        //停止条件:
        //1、遍历到空节点
        if(cur==nullptr)return nullptr;
        //2、找到删除节点,返回值有多种讨论
        if(cur->val==val)
        {
            //(1)当前节点是叶子节点,直接删除
            if(cur->left==nullptr&&cur->right==nullptr)return nullptr;
            //(2)当前节点有一个子节点是空节点,另一个子节点不是空节点,将不是空节点的子节点放到当前位置返回
            else if(cur->left==nullptr&&cur->right!=nullptr)return cur->right;
            else if(cur->left!=nullptr&&cur->right==nullptr)return cur->left;
            //(3)当前节点两个子节点都不是空节点(有两种方式,这里只将大的放到当前位置,小子节点放到大子节点的左下位置)
            else if(cur->left!=nullptr&&cur->right!=nullptr)
            {
                TreeNode* cur2=cur->right;
                while(cur2->left!=nullptr)
                {
                    cur2=cur2->left;
                }
                cur2->left = cur->left;
                return cur = cur->right;
            }
        }
        //按照二叉搜索树的性质去递归找到节点
        if(cur->val>val)
        {
            cur->left=digui(cur->left,val);
        }
        if(cur->valright = digui(cur->right,val);
        }
        return cur;
        
        
    }
    TreeNode* deleteNode(TreeNode* root, int key) {

        return digui(root,key);
        
    }
};

你可能感兴趣的:(算法学习,算法)