代码随想录算法训练营day 22|第六章 二叉树part08

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

相对于 二叉树的最近公共祖先 本题就简单一些了,因为 可以利用二叉搜索树的特性。 

题目链接/文章讲解:代码随想录

视频讲解:二叉搜索树找祖先就有点不一样了!| 235. 二叉搜索树的最近公共祖先_哔哩哔哩_bilibili

这道题明晃晃就要是要利用二叉搜索树的节点大小的特点,如果当前节点的值比这两个的都要大,那就往左边找(单独左子树递归即可),如果当前的值比这两个的值都要小那就往右边找(单独右子树递归即可),如果恰好处于中间的一个值,那就证明当前节点就是最近公共祖先,然后返回给上个节点的递归即可(这么找上个节点必然是这个节点的父母节点,所以必然比这两个数都大或小),再直接返回找到的结果即可——

TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
        // if(root==NULL||root==p||root==q) return root;
        if(root->valval,q->val)){
            return lowestCommonAncestor(root->right,p,q);
        }
        if(root->val>max(p->val,q->val)){
            return lowestCommonAncestor(root->left,p,q);
        }
        return root;
    }

其实,一旦遇到二叉搜索树,迭代法就会很简单,因为二叉搜索树的题它一般都是要利用它的节点大小特点,所以搜索的路径一般都是固定的。这题的思路和递归的一样——

TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
        while(root){
            if(root->val>max(p->val,q->val)) root=root->left;
            else if(root->valval,q->val)) root=root->right;
            else return root;
        }
        return NULL;
    }

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

本题比想象中的简单,大家可以先自己想一想应该怎么做,然后看视频讲解,就发现 本题为什么比较简单了。

题目链接/文章讲解:代码随想录

视频讲解:原来这么简单? | LeetCode:701.二叉搜索树中的插入操作_哔哩哔哩_bilibili

这道题首先说可以不调整节点,就说明可以将所有加入的节点都放在叶子节点处,不调整整棵树,这样就会简单很多。

使用递归的方法,首先要明确递归何时结束,也就是当当前节点为空的时候,就意味着要返回你的新节点了(已经到达了当前树的叶子节点),如果当前不为空,就要判断当前节点的值大于还是小于加入的值,然后直接将递归调用的返回节点作为当前节点的左子树或右子树,然后最后返回整棵树,作为上一层的左子树或右子树——

TreeNode* insertIntoBST(TreeNode* root, int val) {
        if(root==NULL) return new TreeNode(val);
        if(root->val>val) root->left=insertIntoBST(root->left,val);
        if(root->valright=insertIntoBST(root->right,val);
        return root;
    }

迭代法是很简单的,就是找到合适的叶子节点处,然后给这个叶子节点加一个子节点即可——

TreeNode* insertIntoBST(TreeNode* root, int val) {
        if(root==NULL) return new TreeNode(val);
        TreeNode* cur=root;
        TreeNode* pre=NULL;
        while(cur){
            pre=cur;
            if(cur->val>val) cur=cur->left;
            else cur=cur->right;
        }
        cur=new TreeNode(val);
        if(pre->val>val) pre->left=cur;
        else pre->right=cur;
        return root;
    }

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

相对于 插入操作,本题就有难度了,涉及到改树的结构 

题目链接/文章讲解:代码随想录

视频讲解:调整二叉树的结构最难!| LeetCode:450.删除二叉搜索树中的节点_哔哩哔哩_bilibili

这道题其实主要是要搞懂怎么删除节点,要删除的节点分成三种情况:

  1. 左孩子、右孩子送都为NULL的,这样直接delete节点,返回NULL即可,这种最简单。
  2. 左孩子和右孩子有且仅有一个为空,这样直接返回不为空的那个节点即可,记得要delete节点。
  3. 左孩子和右孩子均不为空,这种是最难考虑的,我一开始是想直接找左孩子的最右子节点来作为替换当前节点,但这样要考虑的情况实在太多,而文章则是将左孩子一股脑全给了右孩子的最左节点,这样又简单又能符合要求。
TreeNode* aided(TreeNode* node){
        if(node->left==NULL&&node->right==NULL) {
            delete(node);
            return NULL;
        }
        TreeNode* tmp=node;
        if(node->left&&node->right==NULL){
            tmp=tmp->left;
            delete(node);
            return tmp;
        }
        if(node->right&&node->left==NULL){
            tmp=tmp->right;
            delete(node);
            return tmp;
        }
        tmp=tmp->right;
        while(tmp->left){
            tmp=tmp->left;
        }
        tmp->left=node->left;
        tmp=node->right;
        delete(node);
        return tmp;
    }
    TreeNode* deleteNode(TreeNode* root, int key) {
        if(!root) return NULL;
        if(root->val==key){
            return aided(root);
        }else if(root->val>key) root->left=deleteNode(root->left,key);
        else root->right=deleteNode(root->right,key);
        return root;
    }

文章还给出了删除普通二叉树的方法,就是当要删除的节点本身没有右孩子的时候,直接返回左孩子,如果有右孩子,那就将右孩子的最左边的孩子的值和当前节点的值交换一下,一直交换到目标节点的右孩子为空为止,这样再直接返回目标节点的左孩子。这样做本质上是没有对要删除的节点进行彻底的空间释放——

TreeNode* deleteNode(TreeNode* root, int key) {
        if (root == nullptr) return root;
        if (root->val == key) {
            if (root->right == nullptr) { // 这里第二次操作目标值:最终删除的作用
                return root->left;
            }
            TreeNode *cur = root->right;
            while (cur->left) {
                cur = cur->left;
            }
            swap(root->val, cur->val); // 这里第一次操作目标值:交换目标值其右子树最左面节点。
        }
        root->left = deleteNode(root->left, key);
        root->right = deleteNode(root->right, key);
        return root;
    }

迭代的做法。首先要找到要删除的目标节点,然后再把删除节点之后的子树移接到它的父母节点,如果没有父母节点,就直接返回删除后的子树。删除节点主要考虑到三种情况:

  1. 当前节点为空,也就是没有找到目标节点。
  2. 当前节点的右子树为空,那就直接返回左子树。
  3. 当前节点的右子树不为空,那就将左子树移动到右子树的最左子节点上,其实本质上包含了右子树为空左子树不为空的情况。
class Solution {
private:
    // 将目标节点(删除节点)的左子树放到 目标节点的右子树的最左面节点的左孩子位置上
    // 并返回目标节点右孩子为新的根节点
    // 是动画里模拟的过程
    TreeNode* deleteOneNode(TreeNode* target) {
        if (target == nullptr) return target;
        if (target->right == nullptr) return target->left;
        TreeNode* cur = target->right;
        while (cur->left) {
            cur = cur->left;
        }
        cur->left = target->left;
        return target->right;
    }
public:
    TreeNode* deleteNode(TreeNode* root, int key) {
        if (root == nullptr) return root;
        TreeNode* cur = root;
        TreeNode* pre = nullptr; // 记录cur的父节点,用来删除cur
        while (cur) {
            if (cur->val == key) break;
            pre = cur;
            if (cur->val > key) cur = cur->left;
            else cur = cur->right;
        }
        if (pre == nullptr) { // 如果搜索树只有头结点
            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;
    }
};

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