题目与题解
235. 二叉搜索树的最近公共祖先
题目链接:235. 二叉搜索树的最近公共祖先
代码随想录题解:235. 二叉搜索树的最近公共祖先
解题思路:
有了昨天的二叉树公共祖先的例子,今天这道题就容易很多,并且针对二叉搜索树可以做一些剪枝处理。
同样用递归来做,入参是root和要搜索的节点pq,返回值是当前的公共祖先,终止条件为root等于p或q或root为空(因为一旦p或q等于root,说明它们一方必然是另一方的祖先;如果root为空,说明p和q在root子树中都不存在)。递归体为:如果p和q值都小于root,那就继续将左子树作为root调用递归函数;如果p和q值都大于root,就将右子树作为root调用递归函数;否则p和q必然分别在左右子树中,此时root就是当前最近公共祖先。
class Solution {
public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
if (p == root || q == root || root == null) return root;
if (p.val < root.val && q.val < root.val)
return lowestCommonAncestor(root.left, p, q);
else if (p.val > root.val && q.val > root.val)
return lowestCommonAncestor(root.right, p, q);
else
return root;
}
}
看完代码随想录之后的想法
因为二叉搜索树是有顺序的,所以迭代也很好做,跟递归思路类似,更好写一点。
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 return root;
}
return NULL;
}
};
遇到的困难
有前面的题目打底,这道很容易。
701.二叉搜索树中的插入操作
题目链接:701.二叉搜索树中的插入操作
代码随想录题解:701.二叉搜索树中的插入操作
解题思路:
因为二叉搜索树是有顺序的,所以最简单的做法就是作为叶子节点插入,这样不用更改其他任何节点的结构,挂在某一个有空位的节点下面就好。
这里用递归的写法,入参就是根节点和要插入的节点,终止条件是当前节点为空,就直接return新节点,返回值是插入后的root节点;递归体为:如果当前节点为空返回新节点,如果当前节点值大于插入值,则继续将左子树作为根结点调用递归函数,小于则用右子树调用递归函数。
class Solution {
public TreeNode insertIntoBST(TreeNode root, int val) {
if (root == null) return new TreeNode(val);
if (val < root.val) {
if (root.left == null) root.left = new TreeNode(val);
else insertIntoBST(root.left, val);
} else {
if (root.right == null) root.right = new TreeNode(val);
else insertIntoBST(root.right, val);
}
return root;
}
}
看完代码随想录之后的想法
二叉搜索树用迭代做也比较容易,麻烦的一点在于需要记录一下当前节点的父节点,方便后序赋值。遍历方法为,当前节点不为空时,将parent节点记录为当前节点,如果新的值小于当前节点,使当前节点向左搜索,否则向右搜索。遍历结束后,就会得到应当添加叶子节点的父节点位置,然后再根据该父节点与新节点的值大小,相应添加到左或者右子树上。
class Solution {
public:
TreeNode* insertIntoBST(TreeNode* root, int val) {
if (root == NULL) {
TreeNode* node = new TreeNode(val);
return node;
}
TreeNode* cur = root;
TreeNode* parent = root; // 这个很重要,需要记录上一个节点,否则无法赋值新节点
while (cur != NULL) {
parent = cur;
if (cur->val > val) cur = cur->left;
else cur = cur->right;
}
TreeNode* node = new TreeNode(val);
if (val < parent->val) parent->left = node;// 此时是用parent节点的进行赋值
else parent->right = node;
return root;
}
};
遇到的困难
无
450.删除二叉搜索树中的节点
题目链接:450.删除二叉搜索树中的节点
代码随想录题解:450.删除二叉搜索树中的节点
解题思路:
删除节点与添加节点相比,复杂的地方在于,删除的很可能不是叶子节点,此时就需要调整二叉树的结构,容易出错。
这里还是用递归的方法,入参是root和要删除的值val,终止条件是root为空或root的值等于val,返回值是经过完成删除后的root,递归体较为复杂,如果root值不等于val,则根据val与root的大小,递归用root的左子树或右子树作为入参继续删除;如果root等于val,要分情况讨论:
- 如果root是叶子结点,直接返回null
- 如果root左子树为空,则直接让root等于右子树并返回
- 如果root右子树为空,则直接让root等于左子树并返回
- 如果两个子树都不为空,将左子树挂到右子树最左边的叶子结点左边,然后将root等于右子树并返回
class Solution {
public TreeNode deleteNode(TreeNode root, int key) {
if (root == null) return root;
if (root.val == key) {
if (root.left == null && root.right == null) {
return null;
} else if (root.right != null && root.left == null){
return root.right;
} else if (root.right == null && root.left != null){
return root.left;
} else {
TreeNode cur = root.right;
while (cur.left != null) cur = cur.left;
cur.left = root.left;
return root.right;
}
} else {
if (key < root.val) root.left = deleteNode(root.left, key);
else root.right = deleteNode(root.right, key);
}
return root;
}
}
看完代码随想录之后的想法
一开始的时候没有完全做对,因为忽略了左右子树都存在这种情况,看了随想录的第五种情况才理解,动图还是很不错的。
遇到的困难
root值等于val时,最后一个尤其难理解,我自己写的时候就是没有想到这种情况该怎么处理,所以对于[50,30,70,null,40,60,80],删除节点为50的例子就出错了,因为如果不做转移左子树的操作,在60比70小的情况下,直接让root等于70这个节点,会导致二叉搜索树右子树存在小于根节点的值,与定义相悖了。所以这里需要处理一下,保证结构改变以后也符合二叉搜索树的定义。
今日收获
继续巩固了一下二叉树里面的常用递归操作,学习了如何在二叉搜索树里面增删节点。感觉方法有点万变不离其宗的意思,希望实际面试的时候能写出来。