文章目录
前置知识
参考前文
参考文章:
LeetCode刷题笔记【9】:二叉树专题-1(分别用递归遍历、迭代遍历、标记遍历实现前、中、后序遍历)
LeetCode刷题笔记【10】:二叉树专题-2(二叉树的层序遍历、翻转二叉树、对称二叉树)
LeetCode刷题笔记【10.5】:二叉树专题-2.5(二叉树的层序遍历 - 10道题)
LeetCode刷题笔记【11】:二叉树专题-3(二叉树的最大深度、二叉树的最小深度、完全二叉树的节点个数)
LeetCode刷题笔记【12】:二叉树专题-4(平衡二叉树、二叉树的所有路径、左叶子之和)
LeetCode刷题笔记【13】:二叉树专题-5(找树左下角的值 、路径总和、从前序与中序遍历序列构造二叉树、从中序与后序遍历序列构造二叉树)
LeetCode刷题笔记【14】:二叉树专题-6(最大二叉树 、合并二叉树 、二叉搜索树中的搜索 、验证二叉搜索树)4
LeetCode刷题笔记【15】:二叉树专题-7( 二叉搜索树的最小绝对差、二叉搜索树中的众数、二叉树的最近公共祖先)
235. 二叉搜索树的最近公共祖先
题目描述
LeetCode链接:https://leetcode.cn/problems/lowest-common-ancestor-of-a-binary-search-tree/description/
不管二叉搜索树性质
参考昨天的最后一题
利用二叉搜索树性质进行优化
class Solution {
public:
TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
if(root->val<p->val && root->val<q->val)
return lowestCommonAncestor(root->right, p, q);
else if(root->val>p->val && root->val>q->val)
return lowestCommonAncestor(root->left, p, q);
// else if(root->val介于p->val和q->val之间)
return root;
}
};
701.二叉搜索树中的插入操作
题目描述
LeetCode链接:https://leetcode.cn/problems/insert-into-a-binary-search-tree/description/
搜索到叶节点然后插入
思路: 顺着二叉搜索树一直深度搜索到叶节点, 然后用新建的节点替换原先的一个NULL节点
class Solution {
public:
TreeNode* insertIntoBST(TreeNode* root, int val) {
TreeNode* node = new TreeNode(val);
if(root==nullptr)
return node;
if(val<root->val){
if(root->left==nullptr){
root->left = node;
}else{
insertIntoBST(root->left, val);
}
}else if(val>root->val){
if(root->right==nullptr){
root->right = node;
}else{
insertIntoBST(root->right, val);
}
}
return root;
}
};
利用函数返回值进行操作
以上思路没有问题, 但是以下尝试另一种解法:
以上是在过程中进行操作, 其实并没有用到返回root这个点;
并且各种操作是在叶子节点上进行的, 或许可以尝试在null节点上进行.
class Solution {
public:
TreeNode* insertIntoBST(TreeNode* root, int val) {
if(root==nullptr){
TreeNode* node = new TreeNode(val);
return node;
}
if(val<root->val){
root->left = insertIntoBST(root->left, val);
}else if(val>root->val){
root->right = insertIntoBST(root->right, val);
}
return root;
}
};//但是这种方法其实不如刚才那种方法, 因为遍历的节点更多了?
450.删除二叉搜索树中的节点
题目描述
LeetCode链接:https://leetcode.cn/problems/delete-node-in-a-bst/description/
针对二叉搜索树的解法
① 先搜索到目标节点target
, 然后分情况讨论
首先要注意递归过程中需要cur->left = 递归function(cur->left), cur->right = 递归function(cur->right)
从而可以递归传递结果(这个很像<701. 二叉搜索树中的插入操作>中的操作)
然后:
- 如果左右节点
都NULL
, 直接删除并返回; - 如果左右节点
一个为NULL
, 则返回非NULL的左右节点
; - 如果
左右节点都非NULL
, 那么将cur->left嫁接到cur->right这个子树的最左最深节点下, 作为其左子树, 然后返回cur->right
class Solution {
public:
TreeNode* deleteNode(TreeNode* root, int key) {
if(root==nullptr)
return root;
if(root->val == key){
if(root->left==nullptr && root->right==nullptr){// 左右都为空, 删除root, 返回null
delete root;
return nullptr;
}else if(root->left==nullptr && root->right!=nullptr){// left空right非空, 返回right
TreeNode* tmp = root->right;
delete root;
return tmp;
}else if(root->left!=nullptr && root->right==nullptr){// left非空right空, 返回left
TreeNode* tmp = root->left;
delete root;
return tmp;
}else{// 左右皆非空, 将cur->left嫁接到cur->right这个子树的最左最深节点下, 作为其左子树, 然后返回cur->right
TreeNode* tmp = root->right;
while(tmp->left != nullptr){
tmp = tmp->left;
}
tmp->left = root->left;
tmp = root->right;
delete root;
return tmp;
}
}
if(root->val > key) root->left = deleteNode(root->left, key);
if(root->val < key) root->right = deleteNode(root->right, key);
return root;
}
};
对各类树普世的节点删除方法
② 先搜索到target
, 然后再进行以下操作
如果target->right==nullptr
, 直接删除target
, 返回target->left
如果target->right!=nullptr
, 将target->val
和其右子树的最左的叶子节点的val
交换, 然后继续递归遍历
(之后遍历到这个val==key
的叶子节点时就直接将其删除了)
class Solution {
public:
TreeNode* deleteNode(TreeNode* root, int key) {
if(root==nullptr)
return root;
if(root->val == key){
if(root->right==nullptr){
TreeNode* tmp = root->left;
delete root;
return tmp;
}else{
TreeNode* tmp = root->right;
while(tmp->left){
tmp = tmp->left;
}
swap(tmp->val, root->val);
}
}
root->left = deleteNode(root->left, key);
root->right = deleteNode(root->right, key);
return root;
}
};
// 但是这种方法在后续递归的时候无法利用二叉搜索树的性质, 来减少递归的分支, 开销会增大
总结
之前以为自己对于二叉树的操作已经很熟练了, 最近两天才发现二叉树还有这么多的难点以及新奇的操作与特性;
虽然做起来挺痛苦的, 但也说明有收获, 在进步.
加油吧~