二叉搜索树的最近公共节点
看完题后的思路
从根节点考试查找,找到第一个大于等于p小于等于q的值.
代码
public TreeNode lowestCommonAncestor02(TreeNode root, TreeNode p, TreeNode q) {
int min= Math.min(p.val,q.val);
int max= Math.max(p.val,q.val);
while (root!=null){
if (root.val>=min&&root.val<=max){
return root;
}else if (root.val<min){
root=root.right;
}else {
root=root.left;
}
}
// 没有找到
return null;
}
复杂度
0(logn)
收获
- 从上到下遍历,二叉搜索树中, 在[p,q]区间的节点一定是中间节点吗?
从上到下遍历是,中序遍历不是
- 递归的写法
- node f(root)
- if(root==null) return null
- if(当前节点>两个节点)
left=f(left)
if(left!=null){
return left;
}
if(当前节点<两个节点)
left=f(right)
if(right!=null){
return right;
}
return root
// 递归
public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
if (root==null){
return null;
}
if (root.val>p.val&&root.val>q.val){
TreeNode left = lowestCommonAncestor(root.left, p, q);
if (left!=null){
return left;
}
}
if (root.val<p.val&&root.val<q.val){
TreeNode right = lowestCommonAncestor(root.right, p, q);
if (right!=null){
return right;
}
}
// 当在[p,q]内
return root;
}
因为是搜索一条路径
三刷掌握递归法
二叉搜索树的插入操作
看完题后的思路
观察二叉树可知,找到第一个比自己大的或小的节点插入即可
使用两个指针,pre与curr,当curr为空的时候,pre所指就是插入的位置
易错点
找到的最后一个节点,可能不是根节点
代码
错解
public TreeNode insertIntoBST(TreeNode root, int val) {
TreeNode curr=root;
while (curr!=null){
if (curr.left==null&&curr.right==null){// 比val大或者小的叶子节点
TreeNode node = new TreeNode(val);
if (curr.val>val){
curr.left=node;
}else {
curr.right=node;
}
return root;
}
if (curr.val<val){
curr=curr.right;
}else {
curr=curr.left;
}
}
// root 初始为null
return root;
}
正解
public TreeNode insertIntoBST(TreeNode root, int val) {
if (root==null){
return new TreeNode(val);
}
TreeNode curr=root,pre=null;
while (curr!=null){
pre=curr;
if (curr.val<val){
curr=curr.right;
}else {
curr=curr.left;
}
}
//最后curr为空,pre就是最终插入的位置
TreeNode node = new TreeNode(val);
if (pre.val<val){
pre.right=node;
}else {
pre.left=node;
}
return root;
}
复杂度
收获
- 拓展 如果是排好序的数组,为一个元素查找插入位置
https://editor.csdn.net/md/?articleId=128767732
(1)给定一个升序数组,找到第一个比给定值大的 - 本题使用了二叉搜索树迭代的双指针法
- 二叉搜索树掌握 中序遍历 迭代搜索就行
450.删除二叉搜索树中的二节点
看完题后的思路
一共有五种情况:
本题的难点之一在于如果删除的节点是根节点,pre为空会出现空指针异常
二叉树中的pre的定义
(1)pre初始定义为空,如果不改变二叉树的结构,防止出现空指针异常可以
1)非递归,事先将pre为空的情况排除(空节点,单节点)
例如:
// 二叉搜索树插入节点
public TreeNode insertIntoBST(TreeNode root, int val) {
if (root==null){
return new TreeNode(val);
}
TreeNode curr=root,pre=null;
while (curr!=null){
pre=curr;
if (curr.val<val){
curr=curr.right;
}else {
curr=curr.left;
}
}
//最后curr为空,pre就是最终插入的位置
TreeNode node = new TreeNode(val);
if (pre.val<val){
pre.right=node;
}else {
pre.left=node;
}
return root;
}
- 当是递归时,将为空单独考虑
例如:
// 二叉搜索树众数
public void findModeDG(TreeNode root) {
if (root==null){
return;
}
findModeDG(root.left);
// 没有考虑单节点情况
if (pre==null||pre.val!=root.val){
count=1;
}else{
count++;
}
pre=root;
if (count>maxCount){
findModeRes.clear();// 清空 加新的众数
findModeRes.add(root.val);
maxCount=count;
}else if (count==maxCount){
findModeRes.add(root.val);
}
findModeDG(root.right);
}
3) 当需要修改树的结构,可以构造一个虚拟头结点
例如:
// 450 删除二叉树中的节点
public TreeNode deleteNode(TreeNode root, int key) {
// 特殊情况
if (root==null){
return null;
}
if (root.left==null&&root.right==null&&root.val==key){
return null;
}
// 使用一个虚拟根节点,防止删除根节点时出错
TreeNode iroot=new TreeNode(Integer.MAX_VALUE);
iroot.left=root;
TreeNode curr=root,pre=iroot;
while (curr!=null){
if (curr.val==key){
// 首先知道当前节点是上一节点的左节点还是右节点
boolean isLeft=true;
if (pre.val<curr.val){
isLeft=false;
}
// 1. 为叶子结点
if (curr.left==null&&curr.right==null){
// 直接删除
if (isLeft){
pre.left=null;
}else {
pre.right=null;
}
}else if (curr.left!=null&&curr.right==null){ //2. 左不空 右为空
// 直接删除
if (isLeft){
pre.left=curr.left;
}else {
pre.right=curr.left;
}
}else if (curr.left==null&&curr.right!=null){// 3.左空右不空
// 直接删除
if (isLeft){
pre.left=curr.right;
}else {
pre.right=curr.right;
}
}else { // 4. 左右都不为空 右子树继承
TreeNode left=curr.left;
TreeNode right=curr.right;
// 右子树继承
if (isLeft){
pre.left=right;
}else {
pre.right=right;
}
// pre.right=right;
// 左子树插入右子树最左节点
while (right.left!=null){
right=right.left;
}
right.left=left;
}
return iroot.left;
}
pre=curr;
if (curr.val<key){
curr=curr.right;
}else {
curr=curr.left;
}
}
// 5.curr=null 没有找到
return iroot.left;
}
代码
// 450 删除二叉树中的节点
public TreeNode deleteNode(TreeNode root, int key) {
// 特殊情况
if (root==null){
return null;
}
if (root.left==null&&root.right==null&&root.val==key){
return null;
}
// 使用一个虚拟根节点,防止删除根节点时出错
TreeNode iroot=new TreeNode(Integer.MAX_VALUE);
iroot.left=root;
TreeNode curr=root,pre=iroot;
while (curr!=null){
if (curr.val==key){
// 首先知道当前节点是上一节点的左节点还是右节点
boolean isLeft=true;
if (pre.val<curr.val){
isLeft=false;
}
// 1. 为叶子结点
if (curr.left==null&&curr.right==null){
// 直接删除
if (isLeft){
pre.left=null;
}else {
pre.right=null;
}
}else if (curr.left!=null&&curr.right==null){ //2. 左不空 右为空
// 直接删除
if (isLeft){
pre.left=curr.left;
}else {
pre.right=curr.left;
}
}else if (curr.left==null&&curr.right!=null){// 3.左空右不空
// 直接删除
if (isLeft){
pre.left=curr.right;
}else {
pre.right=curr.right;
}
}else { // 4. 左右都不为空 右子树继承
TreeNode left=curr.left;
TreeNode right=curr.right;
// 右子树继承
if (isLeft){
pre.left=right;
}else {
pre.right=right;
}
// pre.right=right;
// 左子树插入右子树最左节点
while (right.left!=null){
right=right.left;
}
right.left=left;
}
return iroot.left;
}
pre=curr;
if (curr.val<key){
curr=curr.right;
}else {
curr=curr.left;
}
}
// 5.curr=null 没有找到
return iroot.left;
}
复杂度
收获
- 二叉搜索树中pre指针空值处理
- 三刷再刷一遍本题