代码随想录算法训练营第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
    评论
第二十二算法训练营主要涵盖了Leetcode题目的三道题目,分别是Leetcode 28 "Find the Index of the First Occurrence in a String",Leetcode 977 "有序数组的平方",和Leetcode 209 "长度最小的子数组"。 首先是Leetcode 28题,题目要求在给定的字符串找到第一个出现的字符的索引。思路是使用双指针来遍历字符串,一个指向字符串的开头,另一个指向字符串的结尾。通过比较两个指针所指向的字符是否相等来判断是否找到了第一个出现的字符。具体实现的代码如下: ```python def findIndex(self, s: str) -> int: left = 0 right = len(s) - 1 while left <= right: if s[left == s[right]: return left left += 1 right -= 1 return -1 ``` 接下来是Leetcode 977题,题目要求对给定的有序数组的元素进行平方,并按照非递减的顺序返回结果。这里由于数组已经是有序的,所以可以使用双指针的方法来解决问题。一个指针指向数组的开头,另一个指针指向数组的末尾。通过比较两个指针所指向的元素的绝对值的大小来确定哪个元素的平方应该放在结果数组的末尾。具体实现的代码如下: ```python def sortedSquares(self, nums: List[int]) -> List[int]: left = 0 right = len(nums) - 1 ans = [] while left <= right: if abs(nums[left]) >= abs(nums[right]): ans.append(nums[left ** 2) left += 1 else: ans.append(nums[right ** 2) right -= 1 return ans[::-1] ``` 最后是Leetcode 209题,题目要求在给定的数组找到长度最小的子数组,使得子数组的和大于等于给定的目标值。这里可以使用滑动窗口的方法来解决问题。使用两个指针来表示滑动窗口的左边界和右边界,通过移动指针来调整滑动窗口的大小,使得滑动窗口的元素的和满足题目要求。具体实现的代码如下: ```python def minSubArrayLen(self, target: int, nums: List[int]) -> int: left = 0 right = 0 ans = float('inf') total = 0 while right < len(nums): total += nums[right] while total >= target: ans = min(ans, right - left + 1) total -= nums[left] left += 1 right += 1 return ans if ans != float('inf') else 0 ``` 以上就是第二十二算法训练营的内容。通过这些题目的练习,可以提升对双指针和滑动窗口等算法的理解和应用能力。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值