文章: 代码随想录
二叉树中的插入操作:
思路:
中序遍历,一定是升序无重复的 //所以只需要中序遍历两两比较再插入就可以.这种思路需要调整二叉树结构,尝试用过morris自己做不出来. //那么题目也允许另一种思路,在合适的null节点处插入节点,所以可以利用搜索二叉树的特性往下遍历到null节点,最后在这个null节点添加新节点就行。
代码:
public static TreeNode insertIntoBST(TreeNode root, int val) {
TreeNode index=root;
TreeNode indexNode=new TreeNode(val);
if(root==null){return indexNode;}
while (index!=null) {
//利用二叉树特性,比当前节点大就往右找,反之左找
if (index.val < val) {
if (index.right == null) {
index.right = indexNode;
//插入完之后break,不然继续往下传递的话就是index.val = val,没有这个判断逻辑,会陷入死循环。
break;
}else index=index.right;
}
if(index.val>val){
if(index.left==null){
index.left=indexNode;
break;
}else index=index.left;
}
}
return root;
}
二叉搜索树的最近公共祖先:
思路:
当前想法: // 这题为什么不能用morris遍历呢?我觉得是因为中节点是需要接收左右子树的结果做计算才可以的,不像day17/18中最小绝对差,验证二叉树,众数等这些题, // 只需要遍历一遍树,然后更新结构体中的数据.所以这道题是不适合用的 //那这道题和普通的找最近公共祖先有什么区别? //可以利用二叉搜索树的特性,在寻找目标节点p,q的过程中可以减少节点遍历。 //但其实大体思路和昨天找最近公共祖先题目一样。
public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
if(root==null) return null;
//比目标节点大就往左找
if(root.val< p.val && root.val< q.val) return lowestCommonAncestor(root.left,p,q);
//比目标节点小就往右找
if(root.val> p.val && root.val> q.val) return lowestCommonAncestor(root.right,p,q);
//剩下 root==p ||root==q,或者是q<root.val<p或者是p<root.val<q.这三种情况
//为什么这三种情况直接返回root呢?
//首先第一种,这说明另一个节点在这个节点之下,所以直接返回当前节点即可.
//第二三种,因为搜索树的特性,证明这两个目标节点,一个是在当前节点的左子树,一个是在当前节点的右子树,当前节点是不是就是这两个目标节点的公共祖先?
//因为他们向上返回的路径一定会在这个节点相遇吧?
return root;
}
删除二叉搜索树中的节点:
思路:
//删除的时候有几种情况
//1.叶子节点 左为空右也为空 -->直接删除
//2. 左为空,右不为空 或者 右空,左不为空 都是前一个指针跳过当前节点指向不为空的节点.
//3. 左右都不为空 那么这里我们采用左孩子上位模式,那么原本的右孩子放在哪呢?
//我们可以找被删除节点的前驱节点,因为这个前驱节点是当前节点的左子树下最大的一个节点,我们就可以直接把右孩子放在这个节点的右节点上。
//那右孩子不是比这个节点的左孩子任意节点都要大吗?为什么不能放在任意左子树的右节点上呢?
//答案是会破会搜索树性质,很明显,如果不是前驱节点的任何节点,要么右子树不为空无法插入,要么这个节点是上面某一个节点的左子树.
//因为前驱节点是一路.right找到的,其它不是这条线上的节点肯定至少都会存在一次.left,那不就违法搜索树了吗?.left应该是都比这个节点要小的值, //但是我们要插入的右子树是比这整个左子树的任意值都要大的,所以找前驱节点。
代码:
public TreeNode deleteNode(TreeNode root, int key) {
TreeNode cur=root;
TreeNode pre=null;
//先根据搜索树特性找到这个节点
while (cur!=null){
if (cur.val == key) break;
pre=cur;
if(cur.val>key){
cur=cur.left;
}else {
cur=cur.right;
}
}
//如果遍历完了都没找到证明没有
if(cur==null) return root;
// 如果搜索树只有头结点,或者删除的是根节点
if (pre == null) {
return deleteOneNode(cur);
}
//找到了
//判断在找到key时,cur是pre的左孩子还是右孩子
//是左孩子
if(pre.left!=null && pre.left.val==key){
pre.left=deleteOneNode(cur);
}
if(pre.right!=null && pre.right.val==key){
//是右孩子
pre.right=deleteOneNode(cur);
}
return root;
}
private TreeNode deleteOneNode(TreeNode cur) {
//叶子节点,第一种情况
if(cur.left==null && cur.right==null){
cur=null;
}else if(cur.left==null) {cur=cur.right;}
else if(cur.right==null){ cur=cur.left;}
else{
//最后一种情况,左右都不为空
TreeNode mostRight=cur.left;
//找到前驱节点(左子树的最右节点)
while (mostRight.right!=null) mostRight=mostRight.right;
//将当前节点的右子树接入
mostRight.right=cur.right;
//左孩子继位
cur=cur.left;
}
return cur;
}