目录
1.4.1如果该树为空(也就是根节点==null),直接return;
1.4.3.1目标节点的左孩子节点为空 ------cur.left == null
1.4.3.2目标节点的右孩子节点为空 ------cur.right == null
1搜索树
1.1 概念
二叉搜索树又称二叉排序树,它或者是一棵空树,或者是具有以下性质的二叉树:
●若它的左子树不为空,则左子树上所有节点的值都小于根节点的值
●若它的右子树不为空,则右子树上所有节点的值都大于根节点的值
●它的左右子树也分别为二叉搜索树
●二叉搜索树中不存在两个值一样的节点
int[] array ={5,3,4,1,7,8,2,6,0,9};
1.2查找操作
在二叉搜索树中,严格遵守每一个节点的左子树中的值都比该子树的根节点值小,右子树中的值都比该子树的根节点大
所以我们每次查找时,拿要查找的值和节点比较,如果比节点的值小,我们用cur指向该节点的左孩子节点,否则指向该节点的右孩子节点
public boolean search(int val){
TreeNode cur=root;
while(cur!=null){
if(cur.val>val){
cur=cur.left;
}else if (cur.val<val){
cur=cur.right;
}else {
return true;
}
}
return false;
}
故我们在查找节点时,用val值与每一个节点的值进行比较,如果val值比该节点值小,我们就往该节点的左孩子节点走,否则往该节点的右孩子节点走,直到val值与节点值相同,返回true,如果走到null都没有找到val,那么就返回false
1.3插入操作
如果树为空,即根==null,直接插入
if(root==null){
root=new TreeNode(key);
return;
}
2.如果树不是空树,按照查找逻辑确定插入位置,插入新节点
TreeNode node=new TreeNode(key);
TreeNode prv=null;//用prv记录插入节点的上一个节点
TreeNode cur=root;
while(cur!=null){//循环找到插入节点的位置
if(key<cur.val){//如果当前节点的值大于key值,就往当前节点的左孩子节点走,先让prv指向当前节点,再让cur往左孩子节点走
prv=cur;
cur=cur.left;
}else if(key> cur.val){//如果当前节点的值小于key值,就往当前节点的右孩子节点走,先让prv指向当前节点,再让cur往右孩子节点走
prv=cur;
cur=cur.right;
}else {//如果key值与当前节点的val值相等,就直接返回,这是因为一棵二叉搜索树中不能存在两个值一样的节点
return;
}
}
//找到了插入位置,在让key的值比上一个节点prv的值做比较,如果key大于prv的val值,就让key的节点插入到prv的右孩子节点,如果key小于prv的val值,就让key的节点插入到prv的左孩子节点
if(prv.val>key){
prv.left=node;
}else{
prv.right=node;
}
定义两个TreeNode变量,cur用来查找需要插入的位置,prv用来记录cur的上一个节点,当cur为null时,prv就为要插入位置的上一个节点,所以循环条件为cur!=null
在循环中:
1.如果当前节点的值大于key值,就往当前节点的左孩子节点走,先让prv指向当前节点,再让cur往左孩子节点走
2.如果当前节点的值小于key值,就往当前节点的右孩子节点走,先让prv指向当前节点,再让cur往右孩子节点走
3.如果key值与当前节点的val值相等,就直接返回,这是因为一棵二叉搜索树中不能存在两个值一样的节点
4.找到了插入位置,在让key的值比上一个节点prv的值做比较,如果key大于prv的val值,就让key的节点插入到prv的右孩子节点,如果key小于prv的val值,就让key的节点插入到prv的左孩子节点(比较确认新节点应该插入到prv的左孩子节点还是右孩子节点)
1.4删除操作(难点)
1.4.1如果该树为空(也就是根节点==null),直接return;
if(root==null){
return;
}
1.4.2找到要删除的节点以及该节点的父节点
TreeNode prv=null;
TreeNode cur=root;
while(cur!=null){
if(cur.val>val){
prv=cur;
cur=cur.left;
}else if(cur.val<val){
prv=cur;
cur=cur.right;
}else {
break;
}
}
if(cur==null){
return;
}
该操作和上面的查找操作一样,定义两个TreeNode变量,prv指向cur的父节点,cur去循环查找目标节点,查找到了就直接跳出循环,判断一下cur是否为空,如果为空,直接return;
1.4.3找到需要删除的节点时,讨论删除的多种情况
1.4.3.1目标节点的左孩子节点为空 ------cur.left == null
设待删除结点为 cur, 待删除结点的双亲结点为 prv
1. cur 是 root,则 root = cur.right
//要删除节点的左子树为空
if(cur.left==null){
//节点为根节点
if(cur==root){
root=cur.right;
}
2. cur 不是 root,cur 是 parent.left,则 prv.left = cur.right
if(cur==prv.left){
prv.left=cur.right;
}
3. cur 不是 root,cur 是 parent.right,则 parent.right = cur.right
if(cur==prv.right){
prv.right=cur.right;
}
1.4.3.2目标节点的右孩子节点为空 ------cur.right == null
1. cur 是 root,则 root = cur.left
if(cur.right==null){
if(cur==root){
root=cur.left;
}
2. cur 不是 root,cur 是 parent.left,则 parent.left = cur.left
if (cur==prv.left) {
prv.left=cur.left;
}
3. cur 不是 root,cur 是 parent.right,则 parent.right = cur.left
prv.right=cur.left;
注:在讨论目标节点的左右孩子节点到底谁为空时,已经包含了左右孩子节点都为空的情况,故接下来只需要讨论目标节点左右孩子均不为空
1.4.3.3目标节点的左右孩子节点均不为空
目标节点的左子树的最右边节点的值时左子树中最大的,却比右子树中的值都要小,所以将其与目标节点交换,然后按右子树为空删除该节点
亦或者是,找到目标节点的右子树的最左端的节点,因为这个节点是右子树中值最小的节点,却比左子树的所有值都打,将目标节点与其交换,然后按左子树为空删除
上图以左子树中的最大值为例
TreeNode p=cur;
TreeNode c=cur.left;
while(c.right!=null){
p=c;
c=c.right;
}
swap(c,cur);
if(c==p.left){
p.left=c.left;
}
if(c==p.right){
p.right=c.left;
}
因为左子树的最大值可能是目标节点的左孩子节点,所以需要判断一下左子树最大节点与去父节点的关系,如果他是其父节点的左孩子节点,就让其父节点的左孩子节点变为该节点的左孩子节点,否则其父节点的右孩子节点变为该节点的左孩子节点
2.二叉搜索树的完整代码:
package Binary;
import com.sun.source.tree.Tree;
public class BinarySearchTree {
static class TreeNode{
public int val;
public TreeNode left;
public TreeNode right;
public TreeNode(int val) {
this.val = val;
}
}
public TreeNode root;
public void insert(int key){
if(root==null){
root=new TreeNode(key);
return;
}
TreeNode node=new TreeNode(key);
TreeNode prv=null;
TreeNode cur=root;
while(cur!=null){
if(key<cur.val){
prv=cur;
cur=cur.left;
}else if(key> cur.val){
prv=cur;
cur=cur.right;
}else {
return;
}
}
if(prv.val>key){
prv.left=node;
}else{
prv.right=node;
}
}
public boolean search(int val){
TreeNode cur=root;
while(cur!=null){
if(cur.val>val){
cur=cur.left;
}else if (cur.val<val){
cur=cur.right;
}else {
return true;
}
}
return false;
}
public void remove(int val){
if(root==null){
return;
}
TreeNode prv=null;
TreeNode cur=root;
while(cur!=null){
if(cur.val>val){
prv=cur;
cur=cur.left;
}else if(cur.val<val){
prv=cur;
cur=cur.right;
}else {
break;
}
}
if(cur==null){
return;
}
removeNode(prv,cur);
}
private void removeNode(TreeNode prv,TreeNode cur){
//要删除节点的左子树为空
if(cur.left==null){
//节点为根节点
if(cur==root){
root=cur.right;
}else if(cur==prv.right){
prv.right=cur.right;
} else{
prv.left=cur.right;
}
}
//要删除节点的右子树为空
else if(cur.right==null){
if(cur==root){
root=cur.left;
} else if (cur==prv.left) {
prv.left=cur.left;
}else {
prv.right=cur.left;
}
}
//要删除的节点两边都不为空
else {
TreeNode p=cur;
TreeNode c=cur.left;
while(c.right!=null){
p=c;
c=c.right;
}
swap(c,cur);
if(c==p.left){
p.left=c.left;
}
if(c==p.right){
p.right=c.left;
}
}
}
private void swap(TreeNode v1,TreeNode v2){
int tmp= v1.val;
v1.val=v2.val;
v2.val=tmp;
}
}