代码随想录算法训练营第22天 | 235. 二叉搜索树的最近公共祖先 701.二叉搜索树中的插入操作 450.删除二叉搜索树中的节点

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

Alt
这道题感觉迭代法更容易理解,因为二叉搜索树是有序的,是可以自上而下进行遍历的,只要遇到在元素值在 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;
	}
};

二叉搜索树中的插入操作

Alt
不必考虑题目中给出的不同种类的插入方式,我们只需要按照二叉搜索树的规则进行遍历,根据插入元素的值决定递归的方向,遇到空节点时插入就可以了

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;
	}
};

删除二叉搜索树中的节点

Alt
这道题和上一道在二叉搜索树中插入节点是镜像的问题,我们就可以通过相似的思路来解决。用递归的返回值来确定子节点。首先我们需要根节点和删除值两个输入参数,返回一个节点作为上一层的孩子。终止条件就是遇到空节点。单层的处理逻辑有4种情况:

  1. 当前节点的左右孩子都是空,那么直接删除,返回空节点作为上一层的子节点
  2. 当前节点的左孩子为空,右孩子不空,那么用右孩子代替原来的节点位置
  3. 当前节点的右孩子为空,左孩子不空,那么用左孩子代替原来的节点位置
  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;
	}
};
  • 8
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值