235. 二叉搜索树的最近公共祖先
给定一个二叉搜索树, 找到该树中两个指定节点的最近公共祖先。 百度百科中最近公共祖先的定义为:“对于有根树 T 的两个结点 p、q,最近公共祖先表示为一个结点 x,满足 x 是 p、q 的祖先且 x 的深度尽可能大(一个节点也可以是它自己的祖先)。”
235. 二叉搜索树的最近公共祖先 - 力扣(Leetcode)
思路
本题与昨天(day21)的最后一题 236.二叉搜索树的最近公共祖先相比,多了一个限制条件:二叉搜索树。
可以利用二叉搜索树的特性对昨天的算法进行剪枝。该特性是:
对于二叉搜索树的每一个节点,该节点的左子树上的每一个节点(如果存在)都小于该节点的值,该节点的右子树上的每一个节点(如果存在)都大于该节点的值。 且二叉搜索树的每一棵子树都是二叉搜索树。
使用左右中顺序的后序遍历方式,寻找目标的两个节点,由于二叉搜索树中每个节点的值不一样,这道 题可以通过值比较的方式寻找节点。
对于二叉树的一个节点root,p和q中较小值记为low,较大值记为high。
- 如果当前节点就是目标节点,或者是
null,直接返回当前节点。 - 如果当前节点的值
root.val大于high,那么目标节点只可能出现在当前节点的左子树上,因此返回递归遍历左子树的结果即可。 - 如果当前节点的值
root.val小于low,那么目标节点只可能出现在当前节点的右子树上,因此返回递归遍历右子树的结果即可。 - 如果当前节点的值
root.val小于high并且大于low,那么值较小的节点出现在当前节点的左子树上,较大的节点出现在右子树上,因此,当前节点就是公共祖先,且是最近公共祖先,返回当前节点即可。
代码
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode(int x) { val = x; }
* }
*/
class Solution {
public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
int low = p.val > q.val ? q.val : p.val;
int high = p.val > q.val ? p.val : q.val;
return getLowest(root, low, high);
}
private TreeNode getLowest(TreeNode root, int low, int high){
if(root == null || root.val == low || root.val == high){
return root;
}
if(root.val > high){
return getLowest(root.left,low,high);
}
if(root.val < low){
return getLowest(root.right, low, high);
}
return root;
}
}
701.二叉搜索树中的插入操作
给定二叉搜索树(BST)的根节点
root和要插入树中的值value,将值插入二叉搜索树。 返回插入后二叉搜索树的根节点。 输入数据 保证 ,新值和原始二叉搜索树中的任意节点值都不同。 注意,可能存在多种有效的插入方式,只要树在插入后仍保持为二叉搜索树即可。 你可以返回 任意有效的结果 。提示:
- 树中的节点数将在
[0, 104]的范围内。-108 <= Node.val <= 108- 所有值
Node.val是 独一无二 的。-108 <= val <= 108- 保证
val在原始BST中不存在。
701. 二叉搜索树中的插入操作 - 力扣(Leetcode)
思路
- 如果当前节点为
null,那么当前要插入的节点就是二叉树的第一个节点,直接插入即可。 - 否则,判断当前节点
node与val之间的大小关系。- 如果
node.val > val,那么新节点要插入到当前节点的左子树中,- 如果当前节点的左子树为
null,那么新节点就插入到当前节点的左孩子的位置。 - 否则,将当前节点置为当前节点的左孩子,重复当前操作。
- 如果当前节点的左子树为
- 如果
node.val < val,那么新节点要插入到当前节点的右子树中,- 如果当前节点的左子树为
null,那么新节点就插入到当前节点的右孩子的位置。 - 否则,将当前节点置为当前节点的右孩子,重复当前操作。
- 如果当前节点的左子树为
- 如果
代码
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode() {}
* TreeNode(int val) { this.val = val; }
* TreeNode(int val, TreeNode left, TreeNode right) {
* this.val = val;
* this.left = left;
* this.right = right;
* }
* }
*/
class Solution {
public TreeNode insertIntoBST(TreeNode root, int val) {
TreeNode node = root;
if(root == null){
root = new TreeNode(val);
return root;
}
while(node != null){
if(node.val > val){
if(node.left == null){
node.left = new TreeNode(val);
break;
}
node = node.left;
continue;
}
if(node.val < val){
if(node.right == null){
node.right = new TreeNode(val);
break;
}
node = node.right;
}
}
return root;
}
}
450.删除二叉搜索树中的节点
给定一个二叉搜索树的根节点 root 和一个值 key,删除二叉搜索树中的 key 对应的节点,并保证二叉搜索树的性质不变。返回二叉搜索树(有可能被更新)的根节点的引用。
一般来说,删除节点可分为两个步骤:
- 首先找到需要删除的节点;
- 如果找到了,删除它。
450. 删除二叉搜索树中的节点 - 力扣(Leetcode)
思路
删除二叉搜索树只有两个步骤,但过程中却会遇到多种情况,需要分类讨论。
- 二叉树是空树,那么无法对空树进行删除,返回
null。 - 要删除的节点是根节点,
- 如果根节点是一个叶子节点,那么删除后二叉树变成一棵空树,返回
null; - 如果根节点同时存在左右子树,那么,要找到右子树中最小值的节点替代要删除节点。这个节点就是根节点的右子树中最左的节点。有以下几种情况:
- 如果根节点的右孩子只有右子树,没有左子树,那么根节点的右子树中最小值就是这个节点的右孩子,直接将根节点的左子树作为右孩子的左子树,修改后二叉树的根节点是原根节点的右孩子。如图。
- 如果根节点的右孩子同时有左右子树,那么右子树的最小值出现右孩子的左子树中,找到这个节点
node,已知node没有左子树,根据其 有无右子树 分成两种情况: node没有右子树,如图。将node的父节点的左孩子(即node节点)设置为null,将node的值设置到root节点中。node有右子树,如图。将将node的父节点的左孩子(即node节点)设置为node的右孩子,将node的值设置到root节点中。
- 如果根节点的右孩子只有右子树,没有左子树,那么根节点的右子树中最小值就是这个节点的右孩子,直接将根节点的左子树作为右孩子的左子树,修改后二叉树的根节点是原根节点的右孩子。如图。
- 如果根节点只有左子树,那么删除后,二叉树的根节点变成其左孩子;
- 如果根节点只有右子树,那么删除后,二叉树的根节点变成其右孩子;
- 如果根节点是一个叶子节点,那么删除后二叉树变成一棵空树,返回
- 要删除的节点
node不是根节点,- 首先要找到
node的父节点parent,以及标识node是不是parent的左孩子isLeftChild。 - 如果
node是一个叶子节点,那么直接删除该节点,即让node的父节点中node的位置设置为null。 - 如果
node同时存在左右子树,那么,要找到右子树中最小值的节点替代要删除节点。这个节点就是node的右子树中最左的节点。有以下几种情况:- 如果
node的右孩子只有右子树,没有左子树,那么node的右子树中最小值就是这个节点的右孩子,直接将node的右孩子替代node的位置; - 否则,找到
node右子树中最左边的节点,将该节点的数值替换到node中。如果该节点没有右子树,则将其直接删除;否则,将其右孩子替换到node的位置。
- 如果
- 首先要找到
代码
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode() {}
* TreeNode(int val) { this.val = val; }
* TreeNode(int val, TreeNode left, TreeNode right) {
* this.val = val;
* this.left = left;
* this.right = right;
* }
* }
*/
class Solution {
public TreeNode deleteNode(TreeNode root, int key) {
if(root == null){
return null;
}
// 如果要删除的是根节点
if(root.val == key){
if(root.left == null && root.right == null){
return null;
}
if(root.left != null && root.right == null){
// 只有左子树
return root.left;
}
if(root.left == null && root.right != null){
// 只有右子树
return root.right;
}
// 左右子树都有
if(root.right.left == null){
root.right.left = root.left;
return root.right;
}
modifyByLeftmostNode(root);
return root;
}
// 删除的不是根节点
// 找到节点 node 的父节点
TreeNode parent = getParentNode(root,key);
TreeNode node = null;
boolean isLeftChild = false;
// 未找要被删除的节点
if(parent == null){
return root;
}
// 找到被删除的节点
if(parent.left != null && parent.left.val == key){
node = parent.left;
isLeftChild = true;
}else{
node = parent.right;
}
// 如果 node 是叶子,直接删除
if(node.left == null && node.right == null){
if(isLeftChild){
parent.left = null;
}else{
parent.right = null;
}
return root;
}
// 如果 node 有两棵子树
// 找到左子树的最右的节点或右子树最左的节点 , 放到当前节点的位置
if(node.left != null && node.right != null){
getLeftmostInRightSubTree(parent,node,isLeftChild);
return root;
}
// node 只有一棵子树
// 将node的子树只接接到node的父节点
if(node.left != null){
if(isLeftChild){
parent.left = node.left;
}else{
parent.right = node.left;
}
return root;
}
if(node.right != null){
if(isLeftChild){
parent.left = node.right;
}else{
parent.right = node.right;
}
return root;
}
return null;
}
private TreeNode getParentNode(TreeNode node,int key){
while(node != null){
if((node.left != null && node.left.val == key) || (node.right != null && node.right.val == key)){
return node;
}
if(node.left!= null && node.val > key){
// key 在左子树中
node = node.left;
}else{
node = node.right;
}
}
return node;
}
/**
* 找到parent的右子树中最左边的节点, 删除,并返回其值
* parent:当前节点
*/
private void getLeftmostInRightSubTree(TreeNode grandParent,TreeNode parent,boolean isLeftChild){
TreeNode node = parent.right;
if(node.left == null){
// 要删除节点的右孩子就是最小值
if(isLeftChild){
grandParent.left.val = node.val;
parent.right = node.right;
}else{
grandParent.right.val = node.val;
parent.right = node.right;
}
return;
}
modifyByLeftmostNode(parent);
}
private void modifyByLeftmostNode(TreeNode parent){
TreeNode node = parent.right;
while(node.left != null){
// 找到 node.left 是最左边的节点
if(node.left.left == null){
int num = node.left.val;
// 删除 node 的左孩子
if(node.left.right == null){
// 直接删除
node.left = null;
}else{
node.left = node.left.right;
}
parent.val = num;
return;
}
node = node.left;
}
}
}