第二十二天| 235. 二叉搜索树的最近公共祖先 、701.二叉搜索树中的插入操作、450.删除二叉搜索树中的节点

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

题目链接:235 二叉搜索树的最近公共祖先

题干: 给定一个二叉搜索树, 找到该树中两个指定节点的最近公共祖先。

百度百科中最近公共祖先的定义为:“对于有根树 T 的两个结点 p、q,最近公共祖先表示为一个结点 x,满足 x 是 p、q 的祖先且 x 的深度尽可能大(一个节点也可以是它自己的祖先)。”

  • 所有节点的值都是唯一的。
  • p、q 为不同节点且均存在于给定的二叉搜索树中。

思考一:递归法。终止条件:如果当前节点为空,则说明查找失败,返回空。如果当前节点val值在两目标节点val值之间则说明查找成功,返回当前节点。(第二个的终止条件不用写,理由在后面) 单层递归逻辑:若当前节点val值均大于两目标节点val值则递归处理左子树,若当前节点val值均小于两目标节点val值则递归处理右子树。剩下的情况刚好是在中间的情况,直接返回当前节点即可。故第二个终止条件不用写

代码:

class Solution {
public:
    TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
        if (!root)  return nullptr;     //查找到空节点则查找失败
        if (root->val > p->val && root->val > q->val)       //当前节点比目标节点元素值大
            return lowestCommonAncestor(root->left, p, q);
        else if (root->val < p->val && root->val < q->val)        //当前节点比目标节点元素值小
            return lowestCommonAncestor(root->right, p, q);
        else return root;       //查找成功
    }
};

思考二: 迭代法。按递归法修改二叉搜索树搜索的迭代法的处理逻辑即可

二叉搜索树搜索的迭代法:第二十天| 654.最大二叉树、617.合并二叉树、700.二叉搜索树中的搜索、98.验证二叉搜索树

代码:

class Solution {
public:
    TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
        while (root) {
            if (root->val > p->val && root->val > q->val)
                root = root->left;
            else if (root->val < p->val && root->val < q->val)
                root = root->right;
            else break;
        }
        return root;
    }
};

Leetcode 701.二叉搜索树中的插入操作

题目链接:701 二叉搜索树中的插入操作

题干: 给定二叉搜索树(BST)的根节点 root 和要插入树中的值 value ,将值插入二叉搜索树。 返回插入后二叉搜索树的根节点。 输入数据 保证 ,新值和原始二叉搜索树中的任意节点值都不同。

  • 可能存在多种有效的插入方式,只要树在插入后仍保持为二叉搜索树即可。 你可以返回 任意有效的结果 。
  • 给定的树上的节点数介于 0 和 10^4 之间
  • 每个节点都有一个唯一整数值,取值范围从 0 到 10^8
  • -10^8 <= val <= 10^8
  • 新值和原始二叉搜索树中的任意节点值都不同

思考一:递归法-无返回值。按节点有序性考虑,找到匹配位置的叶子节点,val值比较后修改当前节点的孩子节点

代码: 

class Solution {
public:
    void traversal(TreeNode* node, int val) {
        if (node->val > val){
            if (node->left)
                traversal(node->left, val);
            else
                node->left = new TreeNode(val);
        }
        if (node->val < val){
            if (node->right)
                traversal(node->right, val);
            else
                node->right = new TreeNode(val);
        }
    }

    TreeNode* insertIntoBST(TreeNode* root, int val) {
        if (!root)  root = new TreeNode(val);
        traversal(root, val);
        return root;
    }
};

思考二:递归法-无返回值。设置全局变量记录前一个结点作为最后插入的父结点,按节点有序性考虑,找到匹配位置的空节点,val值比较后修改父节点的孩子节点

代码: 

class Solution {
private:
    TreeNode* parent;
    void traversal(TreeNode* cur, int val) {
        if (cur == NULL) {
            TreeNode* node = new TreeNode(val);
            if (val > parent->val) parent->right = node;
            else parent->left = node;
            return;
        }
        parent = cur;
        if (cur->val > val) traversal(cur->left, val);
        if (cur->val < val) traversal(cur->right, val);
        return;
    }

public:
    TreeNode* insertIntoBST(TreeNode* root, int val) {
        parent = new TreeNode(0);
        if (root == NULL) {
            root = new TreeNode(val);
        }
        traversal(root, val);
        return root;
    }
};

思考三:递归法-有返回值。按节点有序性考虑,终止条件:查找到空节点创建新节点返回。单层递归逻辑:val值比较后修改每个当前节点左右孩子节点。

代码:

class Solution {
public:
    TreeNode* insertIntoBST(TreeNode* root, int val) {
        if (!root) {        //插入
            TreeNode* node = new TreeNode(val);
            return node;
        }
        if (root->val > val) root->left = insertIntoBST(root->left, val);
        if (root->val < val) root->right = insertIntoBST(root->right, val);
        return root;
    }
};

思考四:迭代法。按递归法修改二叉搜索树搜索的迭代法的处理逻辑即可,查询并记录父节点,比较val值后插入。

 二叉搜索树搜索的迭代法:第二十天| 654.最大二叉树、617.合并二叉树、700.二叉搜索树中的搜索、98.验证二叉搜索树

代码:

class Solution {
public:
    TreeNode* insertIntoBST(TreeNode* root, int val) {
        TreeNode* node = new TreeNode(val);
        if (!root)  return node;
        TreeNode* parent = root;        //记录前一个节点
        TreeNode* cur = root;
        while (cur) {
            parent = cur;
            if(cur->val > val)  cur = cur->left;
            else cur = cur->right;
        }
        if (parent->val > val) parent->left = node;
        else parent->right = node;
        return root;
    }
};

Leetcode 450.删除二叉搜索树中的节点

题干链接:450 删除二叉搜索树中的节点

题干:给定一个二叉搜索树的根节点 root 和一个值 key,删除二叉搜索树中的 key 对应的节点,并保证二叉搜索树的性质不变。返回二叉搜索树(有可能被更新)的根节点的引用。

一般来说,删除节点可分为两个步骤:

  1. 首先找到需要删除的节点;
  2. 如果找到了,删除它。
  • 节点值唯一
  • root 是合法的二叉搜索树

思考一:递归法。终止条件:遇到空节点则说明要删除的节点在二叉树中未查询到。遇到要删除的节点,进行删除逻辑(五种情况分别处理)。单层递归逻辑:比较val值判断递归处理左孩子节点还是右孩子节点。

二叉搜索树中删除节点遇到的情况:

  • 第一种情况:没找到删除的节点,遍历到空节点直接返回了
  • 找到删除的节点
    • 第二种情况:左右孩子都为空(叶子节点),直接删除节点, 返回NULL为根节点
    • 第三种情况:删除节点的左孩子为空,右孩子不为空,删除节点,右孩子补位,返回右孩子为根节点
    • 第四种情况:删除节点的右孩子为空,左孩子不为空,删除节点,左孩子补位,返回左孩子为根节点
    • 第五种情况:左右孩子节点都不为空,则将删除节点的左子树头结点(左孩子)放到删除节点的右子树的最左面节点的左孩子(直接后驱)上,返回删除节点右孩子为新的根节点。(当然将删除节点的右子树头结点(右孩子)放到删除节点的左子树的最右面节点的右孩子(直接前驱)上也行)

代码: 

class Solution {
public:
    TreeNode* deleteNode(TreeNode* root, int key) {
        if (!root)  return nullptr;     //情况一:不存在目标节点
        if (root->val == key) {
            if (!root->left && !root->right) {      //情况二:左右孩子均不存在
                delete root;
                return nullptr;
            } else if (!root->left) {     //情况三:左孩子不存在,右孩子存在
                auto right = root->right;
                delete root;
                return right;
            } else if (!root->right) {      //情况四:左孩子存在,右孩子不存在
                auto left = root->left;
                delete root;
                return left;
            } else {      //情况五:左右孩子均存在
                TreeNode* cur = root->right;
                while (cur->left)       //寻找右子树最左下节点
                    cur = cur->left;
                cur->left = root->left;     //将目标节点左子树作为右子树最左下节点的左子树
                TreeNode* tmp = root;
                root = root->right;     //替换节点
                delete tmp;
                return root;
            }
        }
        if (root->val > key) root->left = deleteNode(root->left, key);
        if (root->val < key) root->right = deleteNode(root->right, key);
        return root;
    }
};

思考二:迭代法。先寻找目标节点及其父节点,再按上法删除节点处理逻辑处理即可。

代码: 

class Solution {
public:
    TreeNode* deleteTarget(TreeNode* node) {      //删除二叉搜索树的目标节点
        if (!node)  return nullptr;     //情况二
        if (!node->left) return node->right;        //情况三
        if (!node->right) return node->left;        //情况四

        //情况五
        TreeNode* cur = node->right;
        while (cur->left) 
            cur = cur->left;
        cur->left = node->left;
        return node->right;
    }

    TreeNode* deleteNode(TreeNode* root, int key) {
        if (!root)  return nullptr;
        TreeNode* cur = root;       //记录
        TreeNode* pre = nullptr;        //记录父节点
        while (cur) {       //寻找目标节点即其父节点
            if (cur->val == key) break;
            pre = cur;
            if (cur->val > key) cur = cur->left;
            else cur = cur->right;
        }

        if (!pre)     //二叉搜索树只要一个节点
            return deleteTarget(cur);     
        //确定要删除的是左孩子还是右孩子
        if (pre->left && pre->left->val == key)
            pre->left = deleteTarget(cur);
        if (pre->right && pre->right->val == key)
            pre->right = deleteTarget(cur);

        return root;
    }
};

自我总结:

  • 熟悉节点有序性的用法
  • 了解二叉树结构的调整的思路

你可能感兴趣的:(代码随想录算法训练营,leetcode,算法)