二叉搜索树的最近公共祖先
这道题感觉迭代法更容易理解,因为二叉搜索树是有序的,是可以自上而下进行遍历的,只要遇到在元素值在 p、q 之间([p, q])的节点就可以返回,因为这就是两个目标节点的最近公共祖先。
class Solution{
public:
TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q){
while(root){
if(p->val < root->val && q->val < root->val){
root = root->left;
}
else if(p->val > root->val && q->val > root->val){
root = root->right;
}
else return root; // 如果p和q一个在左一个在右,或者就在当前节点,那么当前节点就是公共祖先
}
return nullptr;
}
};
递归写法,我们知道 BST 中一定会找到这两个节点,所以一定会返回非空节点。我们不需要对中间节点进行处理,所以使用哪种遍历方法都是可以的。
class Solution{
public:
TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q){
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* insertIntoBST(TreeNode* root, int val){
if(root == nullptr){
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;
}
};
我们用root->left
这样的形式接收来自下一层返回的节点,这是一个很巧妙的写法,如果没有返回值,就需要一直记录遍历到的节点作为父节点,以便在递归到空节点时插在其下面。这明显要更加复杂的。
按照上面说的无返回值的逻辑我们可以写出迭代法代码。
class Solution{
public:
TreeNode* insertIntoBST(TreeNode* root, int val){
if(root == nullptr){
root = new TreeNode(val);
return root;
}
TreeNode* cur = root;
TreeNode* parent;
while(cur != nullptr){
parent = cur;
if(val > cur->val) cur = cur->right;
else cur = cur->left; // 这里要注意用if-else,因为第一个if判断会改变cur
}
TreeNode* node = new TreeNode(val);
if(val > parent->val) parent->right = node;
else parent->left = node;
return root;
}
};
删除二叉搜索树中的节点
这道题和上一道在二叉搜索树中插入节点是镜像的问题,我们就可以通过相似的思路来解决。用递归的返回值来确定子节点。首先我们需要根节点和删除值两个输入参数,返回一个节点作为上一层的孩子。终止条件就是遇到空节点。单层的处理逻辑有4种情况:
- 当前节点的左右孩子都是空,那么直接删除,返回空节点作为上一层的子节点
- 当前节点的左孩子为空,右孩子不空,那么用右孩子代替原来的节点位置
- 当前节点的右孩子为空,左孩子不空,那么用左孩子代替原来的节点位置
- 当前节点的左右孩子都不为空,有一种做法就是将左子树接到右子树最左侧节点的左孩子上(二叉搜索树的性质)
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){
delete root;
return nullptr;
}
else if(root->left == nullptr && root->right != nullptr){
auto retNode = root->right;
delete root;
return retNode;
}
else if(root->right == nullptr && root->left != nullptr){
auto retNode = root->left;
delete root;
return retNode;
}
else{
TreeNode* cur = root->right;
while(cur->left){
cur = cur->left;
}
cur->left = root->left;
TreeNode* tmp = root;
root = root->right;
delete tmp; // 将原root空间释放
return root;
}
}
if(key < root->val) root->left = deleteNode(root->left, key); // 如果还没找到目标值,根据BST性质向左右遍历
if(key > root->val) root->right = deleteNode(root->right, key);
return root;
}
};
迭代法
class Solution{
public:
TreeNode* deleteOneNode(TreeNode* target){
if(target == NULL) return target;
if(target->right == NULL) return target->left;
TreeNode* cur = target->right;
while(cur->left){
cur = cur->left;
}
cur->left = target->left;
return target->right;
}
TreeNode* deleteNode(TreeNode* root, int key){
if(!root) return root;
TreeNode* cur = root; // 从根节点寻找目标节点
TreeNode* pre = NULL; // 记录目标节点的父节点,以便删除目标节点
while(cur){
if(cur->val == key) break;
pre = cur;
if(cur->val > key) cur = cur->left;
else cur = cur->right;
}
if(pre == NULL){
// 如果父节点是空,说明树中只有一个根节点,且该节点就是目标值
return deleteOneNode(cur);
}
else if(pre->left && pre->left->val == key){
// 查看当前节点是在父节点的左边还是右边,并且要确定找到了,再去删除
pre->left = deleteOneNode(cur);
}
else if(pre->right && pre->right->val == key){
pre->right = deleteOneNode(cur);
}
return root;
}
};