LeetCode刷题day42

本日刷题内容二叉搜索树节点的插入,删除,最近公共祖先问题

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

题目描述

给定一个二叉搜索树, 找到该树中两个指定节点的最近公共祖先。

百度百科中最近公共祖先的定义为:“对于有根树 T 的两个结点 p、q,最近公共祖先表示为一个结点 x,满足 x 是 p、q 的祖先且 x 的深度尽可能大(一个节点也可以是它自己的祖先)。”

例如,给定如下二叉搜索树: root = [6,2,8,0,4,7,9,null,null,3,5]

在这里插入图片描述

示例 1:
输入: root = [6,2,8,0,4,7,9,null,null,3,5], p = 2, q = 8
输出: 6 

解释: 节点 2 和节点 8 的最近公共祖先是 6。

示例 2:
输入: root = [6,2,8,0,4,7,9,null,null,3,5], p = 2, q = 4
输出: 2

解释: 节点 2 和节点 4 的最近公共祖先是 2, 因为根据定义最近公共祖先节点可以为节点本身。

思路分析

做过二叉树:公共祖先问题的同学应该知道,利用回溯从低向上搜索,遇到一个节点的左子树有p,右子树有q,那么当前节点就是最近公共祖先.
但是本题是二叉搜索树,是有序的,其处理思路可以更简单.
只要从上到下遍历的时候,cur节点的数值在[p,q]区间中,就可说明该节点cur就是最近公共祖先了.

那么我们可以采用递归遍历(以先序遍历为例):
如图所示:p为节点3,q为节点5
在这里插入图片描述
可以看出直接按照指定的方向,就可以找到节点4,为最近公共祖先,而且不需要遍历整棵树,找到结果直接返回!

方法1:递归

递归三部曲:

  • 参数和返回值:参数为递归的根节点以及两个节点p,q; 返回值为查找到的最近公共祖先
  • 结束条件:当节点为空则结束即可.
  • 每一层递归逻辑:如果cur->val 大于p->val和q->val,那么应该向左遍历,如果 cur->val小于p->val和q->val,则应该向右遍历. 如果在两者之间,则该节点即是最近公共祖先节点,直接返回该节点即可.
参考代码1
//递归法   主要是寻找区间 [p,q],root在这个区间之间,那么root就是公共祖先. 
TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
	if(root==NULL){
		return NULL;
	}
	//左子树
	if(root->val > p->val && root->val > q->val){//注意p,和q的大小是未知的,所以两个都要进行比较 
		TreeNode* left = lowestCommonAncestor(root->left,p,q);//遍历一条边的做法 
		if(left){//如果最后不为空则说明找到了,则直接进行返回 
			return left;
		} 
	} 
	
	if(root->val < p->val && root->val < q->val){
		TreeNode* right = lowestCommonAncestor(root->right,p,q);
		if(right){
			return right;
		}
	} 
	return root;//如果root->val 即不都大于p,q;又不都小于p,q; 则说明root肯定是位于p,q之间的 
}
方法2:迭代法

利用二叉搜索树的有序性来寻找即可.

参考代码2
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{ // root在区间中间 
			return root;
		}
	} 
	return NULL; 
} 

701.二叉搜索树中的插入操作

题目描述

给定二叉搜索树(BST)的根节点和要插入树中的值,将值插入二叉搜索树。 返回插入后二叉搜索树的根节点。 输入数据 保证 ,新值和原始二叉搜索树中的任意节点值都不同。

注意,可能存在多种有效的插入方式,只要树在插入后仍保持为二叉搜索树即可。 你可以返回 任意有效的结果 。

示例 1:

在这里插入图片描述

输入:root = [4,2,7,1,3], val = 5
输出:[4,2,7,1,3,5]

解释:另一个满足题目要求可以通过的树是:

在这里插入图片描述

思路分析

这个题虽然有多种答案,当时我们在解决时只需要 按照二叉搜索树的规则去遍历,遇到空节点插入节点即可.
在这里插入图片描述

方法一:递归(很野的路子)

如果要向root的左子树遍历,但是root->left为空,则直接插入节点返回即可,如果不为空则继续遍历.
同理遍历右子树.

参考代码
//递归法
void traversal(TreeNode* root,int val) {
	if(val < root->val) { //递归法的野路子.. 
		if(!root->left) {//直接在本层判断左子树是否为空 
			root->left = new TreeNode(val);
		} else {
			traversal(root->left,val);
		}
	}
	if(val > root->val) {
		if(!root->right) {//在递归之前判断右子树是否为空.. 
			root->right = new TreeNode(val);
		} else {
			traversal(root->right,val);
		}
	}
	return;
}


TreeNode* insertIntoBST(TreeNode* root, int val) {
	if(root==NULL) {
		root = new TreeNode(val);
	}
	traversal(root,val);
	return root;
}
方法二:递归(不使用返回值)

插入节点,可以用俩辅助节点parent,cur节点.每次cur为空时,便是需要插入节点了,根绝parent->val和val的大小来进行插入的位置.

参考代码2
TreeNode* parent = new TreeNode(0);
void traversal(TreeNode* cur,int val) {
	if(cur == NULL){
		TreeNode* node = new TreeNode(val);
		if(val > parent->val){
			parent->right = node;
		}else{
			parent->left  = node;
		}
		return;
	}
	parent = cur;
	if(val < cur->val){
		traversal(cur->left,val);
	}else if(val > cur->val){
		traversal(cur->right,val);
	}
	return;
}

TreeNode* insertIntoBST(TreeNode* root, int val) {
	if(root==NULL){
		root = new TreeNode(0);
	} 
	traversal(root,val);
	return root;
}
方法三:递归

递归三要素:

  • 参数和返回值:参数为递归子树的根节点以及要插入的节点值.
  • 结束条件:当根节点为空时,插入节点,递归结束.
  • 单层递归逻辑: 如果val < root->val,向左递归. val > root->val,向右递归.
参考代码3
TreeNode* insertIntoBST(TreeNode* root, int val) {
	if(root==NULL){//如果节点为空了,则直接以val创建节点并返回 
		return new TreeNode(val);
	} 
	if(val < root->val){//向左子树寻找插入的位置. 
		root->left = insertIntoBST(root->left,val);
	}
	if(val > root->val){//向右边子树寻找插入的位置
		root->right = insertIntoBST(root->right,val);
	}
	return root;
}
方法四:迭代

思路和不用返回值的递归类似:先找到要插入的位置的父节点,然后根据大小进行插入.

参考代码4
//迭代法 
TreeNode* insertIntoBST(TreeNode* root, int val) {
	if(root==NULL){
		root = new TreeNode(val);
		return root;
	}
	TreeNode* pre=NULL;//插入节点一般就定义两个变量,不要在想着用野套路本次判断下一次然后插入节点...太混乱了. 
	TreeNode* cur = root;
	while(cur!=NULL){
		pre = cur;//更新上一个节点  必须在这里进行更新,因为下面cur就发生变化了 
		if(val < cur->val){//这里不可以分开成两个if哦!因为一旦分开,第二个if中的cur就和第一个if中的cur不同了,很可能出错. 
			cur = cur->left;
		}else if(val > cur->val){
			cur = cur->right;
		} 
	}
	
	//在pre上插入val节点
	if(val < pre->val){
		pre->left = new TreeNode(val);
	}else if(val > pre->val){
		pre->right = new TreeNode(val);
	} 
	return root;
} 

450. 删除二叉搜索树中的节点

题目描述

给定一个二叉搜索树的根节点 root 和一个值 key,删除二叉搜索树中的 key 对应的节点,并保证二叉搜索树的性质不变。返回二叉搜索树(有可能被更新)的根节点的引用。

一般来说,删除节点可分为两个步骤:

首先找到需要删除的节点;
如果找到了,删除它。

示例 1:

在这里插入图片描述

输入:root = [5,3,6,2,4,null,7], key = 3
输出:[5,4,6,2,null,null,7]

解释:给定需要删除的节点值是 3,所以我们首先找到 3 这个节点,然后删除它。
一个正确的答案是 [5,4,6,2,null,null,7], 如上图所示。
另一个正确答案是 [5,2,6,null,4,null,7],如下图所示
在这里插入图片描述

示例 2:
输入: root = [5,3,6,2,4,null,7], key = 0
输出: [5,3,6,2,4,null,7]

解释: 二叉树不包含值为 0 的节点

示例 3:
输入: root = [], key = 0
输出: []
方法一:递归

递归三部曲:

  • 确定参数和返回值:参数便是遍历的子树的根节点和要删除的节点值. 返回值是删除节点后子树的根节点.
  • 结束条件:如果root==null,则说明没找到要删除的节点,直接返回
  • 确定单层递归逻辑:

有以下三种情况:

第一种情况:没找到删除的节点,遍历到空节点直接返回.

第二种情况:找到了删除的节点,对于删除操作要分成四种情况,并且每次都要返回当前子树 的根节点给上一层

  • 左右孩子都为空(叶子节点),直接删除节点, 返回NULL为根节点
  • 删除节点的左孩子为空,右孩子不为空,删除节点,右孩子补位,返回右孩子为根节点
  • 删除节点的右孩子为空,左孩子不为空,删除节点,左孩子补位,返回左孩子为根节点
  • 左右孩子节点都不为空,则将删除节点的左子树头结点(左孩子)放到删除节点的右子树的最左面节点的左孩子上,返回删除节点右孩子为新的根节点。

第三种情况:暂时还没有找到要删除的节点则继续进行递归寻找

左右孩子都不为空的情况有点难以理解,看下面动画:
在这里插入图片描述

参考代码
TreeNode* deleteNode(TreeNode* root, int key) {
	// 第一种情况:没找到删除的节点 ,遍历到空节点直接返回NULL
	if(root==NULL) {
		return root;
	}
	//第二种情况:找到了删除的节点,对于删除操作要分成四种情况,并且每次都要返回当前子树 的根节点给上一层.(因为删除节点后指向会变更)
	if(root->val == key) {
		if(!root->left && !root->right) { //1.当左右孩子都为空,直接删除节点,返回NULL
			delete root;
			return NULL;
		} else if(!root->right) {//2.当左孩子不为空,右孩子为空,删除节点左孩子补位.返回左孩子
			TreeNode* temp = root->left;
			delete root;
			return temp;
		} else if(!root->left) { //3.当右孩子不为空,左孩子为空,删除节点右孩子补位.返回右孩子
			TreeNode* temp = root->right;
			delete root;
			return temp;
		} else {//4.当左右孩子都不为空,则将删除节点的左子树 移到删除节点右子树的最左孩子位置上;并返回删除节点的右子树
			TreeNode *cur = root->right;
			while(cur->left) { //寻找删除节点的最左侧节点
				cur = cur->left;
			}
			cur->left = root->left;//移到删除节点右子树的最左孩子位置上
			TreeNode* temp = root->right;
			delete root;
			return temp;
		}
	}

	//第三种情况:暂时还没有找到要删除的节点
	if(key < root->val) {
		root->left = deleteNode(root->left,key);
	} else if(key > root->val) {
		root->right = deleteNode(root->right,key);
	}
	return root;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

爱编程的大李子

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值