总链接:腾讯文档
701. 二叉搜索树中的插入操作
链接:力扣
答案:虽然一次做对,但这道题中间的思路算不上清晰。一开始我又像前两道题一样设置了pre指针指向空节点,因为我想 在
加判断,看pre是否为空,空的话返回root,不空判断pre的val和插入val之间的大小。后来发现这个判断很重复,而且root非空的情况也没法写。另外我判断这个插入是从上至下的,因此用到了相当于前序遍历,没有很复杂。
/** * 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* insertIntoBST(TreeNode* root, int val) { if(root==nullptr) { TreeNode *node=new TreeNode(val); return node; } else { if(val>root->val) { root->right=insertIntoBST(root->right,val); } else { root->left=insertIntoBST(root->left,val); } return root; } } };
450.删除二叉搜索树中的节点
二叉搜索树定义:若它的左子树不空,则左子树上所有结点的值均小于它的根结点的值; 若它的右子树不空,则右子树上所有结点的值均大于它的根结点的值; 它的左、右子树也分别为二叉排序树。
链接:代码随想录
想的完全错误,逻辑上就不对啊啊啊啊啊啊啊啊!!!!重做重做
注:错误想法
/** * 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) {} * }; */ /* 厘清逻辑: 1、找到要删除的节点node(利用二叉搜索树的性质,写个search函数) 2、判断node是否为root,如果是,则返回nullptr 否则,找node右子树的最小值,或者左子树的最大值进行替补 (具体为:右子树不为空,右子树进行替补 else if左子树不为空,左子树进行替补 else(两个都为空,直接删除节点,无需更改结构) */ class Solution { public: TreeNode *pre=nullptr; TreeNode* deleteNode(TreeNode* root, int key) { TreeNode *node=Findkey(root,key); /*if(pre!=nullptr && node!=nullptr) { cout<<pre->val<<" "<<node->val<<endl; }*/ if(pre==nullptr || node==nullptr)//没找到要删的节点,同时也包含根节点为空的情况 { return root; } else//找node右子树的最小值,或者左子树的最大值进行替补 { if(node->right!=nullptr)//node右子树不为空,找右子树最小值进行替补 { if(pre->left==node) { pre->left= Find_RightTree_Min(node); } else if(pr->right==node) { pre->right= Find_RightTree_Min(node); } } else//node右子树为空,直接用node左节点进行替补(即使左子树为空也符合条件!) { if(pre->left==node) { pre->left=node->left; } else if(pre->right==node) { pre->right=node->left; } } } */ } TreeNode* Findkey(TreeNode *root,int key) { if(root==nullptr) { return nullptr; } else { if(key==root->val) { return root; } else if(key>root->val) { pre=root; root=Findkey(root->right,key); } else { pre=root; root=Findkey(root->left,key); } return root; } } TreeNode *Find_RightTree_Min(TreeNode *node) };
正确解法:我的想法基本正确,问题是对递归了解不够深刻,在递归中我一直觉得还要记录被删除节点的上一个节点,但是实际上这个过程包含在递归的上一层。
解法一:代码随想录中给出一种解法,但是这种解法只能不断增加树的高度。最后的树是高度不平衡的。
代码随想录里的思路是:当要删除的节点左右子树都不为空时,找到右子树中最左端节点,把待删除节点的整个左子树转移过去,作待删除节点右子树的最左端节点的左子树。
如图:
代码如下,只做参考
if (root->val == key) { // 第二种情况:左右孩子都为空(叶子节点),直接删除节点, 返回NULL为根节点 // 第三种情况:其左孩子为空,右孩子不为空,删除节点,右孩子补位 ,返回右孩子为根节点 if (root->left == nullptr) return root->right; // 第四种情况:其右孩子为空,左孩子不为空,删除节点,左孩子补位,返回左孩子为根节点 else if (root->right == nullptr) return root->left; // 第五种情况:左右孩子节点都不为空,则将删除节点的左子树放到删除节点的右子树的最左面节点的左孩子的位置 // 并返回删除节点右孩子为新的根节点。 else { TreeNode* cur = root->right; // 找右子树最左面的节点 while(cur->left != nullptr) { cur = cur->left; } cur->left = root->left; // 把要删除的节点(root)左子树放在cur的左孩子的位置 TreeNode* tmp = root; // 把root节点保存一下,下面来删除 root = root->right; // 返回旧root的右孩子作为新root delete tmp; // 释放节点内存(这里不写也可以,但C++最好手动释放一下吧) return root; } }
解法二
链接:东哥带你刷二叉搜索树(基操篇) :: labuladong的算法小抄
关键代码:
来明白了思路试着写出我的代码!show me the code!
写出大体框架,这道题比较特别的一点是,找到要删除的节点、和确定的去删除并改造二叉树,都需要递归。
class Solution { public: TreeNode* deleteNode(TreeNode* root, int key) { if(root==nullptr) { return root; } else { if(key==root->val)//找到要删除的节点啦 { } else if(key<root->val)//在左子树中找要删除的节点 { root->left=deleteNode(root->left,key); } else { root->right=deleteNode(root->right,key); } return root; } } };
补充
/** * 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* deleteNode(TreeNode* root, int key) { if(root==nullptr) { return root; } else { if(key==root->val)//找到要删除的节点啦 { if(root->left==nullptr && root->right==nullptr)//第一种情况,叶子节点 { return nullptr; } else if(root->left!=nullptr && root->right==nullptr) { return root->left; } else if(root->right!=nullptr && root->left==nullptr) { return root->right; } else//要删除的节点的左右子节点都不为空 { //找到右子树的最小节点 TreeNode *node =Find_RightTree_min(root->right); int temp=root->val; root->val=node->val; node->val=temp; root->right=deleteNode(root->right,temp); return root; } } else if(key<root->val)//在左子树中找要删除的节点 { root->left=deleteNode(root->left,key); } else { root->right=deleteNode(root->right,key); } return root; } } TreeNode* Find_RightTree_min(TreeNode *node) { // BST 最左边的就是最小的 while (node->left != nullptr) node = node->left; return node; } };
他人总结:
- 方法1:可能增加树的高度:----------------------代码随想录
- 如果目标节点大于当前节点值,则去右子树中删除;
- 如果目标节点小于当前节点值,则去左子树中删除;
- 如果目标节点就是当前节点,分为以下三种情况:
- 其无左子:其右子顶替其位置,删除了该节点;
- 其无右子:其左子顶替其位置,删除了该节点;
- 其左右子节点都有:其左子树转移到其右子树的最左节点的左子树上,然后右子树顶替其位置,由此删除了该节点。
2、方法2: 不会增加树的高度--------------------------labuladong
- 如果目标节点没有子节点,我们可以直接移除该目标节点。
- 如果目标节只有一个子节点,我们可以用其子节点作为替换。
- 如果目标节点有两个子节点,我们需要用其中序后继节点或者前驱节点来替换,再删除该目标节点。(巧妙地利用删除递归本身,删除要替换的前驱节点)