二叉搜索树
一、概念
二叉搜索树又称二叉排序树,它或者是一棵空树,或者是具有以下性质的二叉树:
若它的左子树不为空,则左子树上所有节点的值都小于根节点的值
若它的右子树不为空,则右子树上所有节点的值都大于根节点的值
它的左右子树也分别为二叉搜索树
二叉搜索树中序遍历是有序的结构
如下图所示就是一个二叉搜索树:
二、搜索树操作
1.查找节点
1.如果根节点的值等于要查找的值,返回value所在的节点
2.如果根节点的值大于要查找的值,那么在其左子树查找
3.如果根节点的值小于要查找的值,那么在其右子树查找
4.没找到返回null
代码实现:
class Node{
int val;
Node left;
Node right;
public Node(int val){
this.val = val;
left = right = null;
}
}
public class BSTree {
private Node root = null;
//在搜索书中查找val,找到返回val所在的节点,没找到的话返回null
public Node find(int val){
if(root == null)
return null;
Node cur = root;
while(cur != null){
//找到返回节点,如果当前节点值大于val,向搜索树左边遍历,反之向右遍历
if(cur.val == val){
return cur;
}else if(cur.val > val){
cur = cur.left;
}else{
cur = cur.right;
}
}
return null;
}
}
2.插入节点
插入分为两种情况:
1.如果树为空树,即根 == null,直接插入
2.如果树不是空树,按照查找逻辑确定插入位置,插入新结点,插入的地方都是叶子节点的位置
代码实现:
//向二叉搜索书中插入节点,插入成功返回true,失败返回false
public boolean insert(int val){
if(root == null){
root = new Node(val);
return true;
}
Node cur = root;
Node parent = null;
//搜索适合插入的位置(都是叶子节点的位置)
while(cur != null){
parent = cur;
//不能有值相等的节点
if(cur.val == val){
return false;
}else if(cur.val > val){
cur = cur.left;
}else{
cur = cur.right;
}
}
//如果parent的值大于val插入parent左边,反之插入右边
Node node = new Node(val);
if(parent.val > val){
parent.left = node;
}else{
parent.right = node;
}
return true;
}
3.删除节点
设待删除结点为 cur, 待删除结点的双亲结点为 parent
删除节点主要分为三种情况:
1.要删除的节点只有左孩子,即cur.right == null;
- cur 是 root,则 root = cur.left
- cur 不是 root,cur 是 parent.left,则 parent.left = cur.left
- cur 不是 root,cur 是 parent.right,则 parent.right = cur.left
2.要删除的节点只有右孩子,即cur.left == null;
- cur 是 root,则 root = cur.right
- cur 不是 root,cur 是 parent.left,则 parent.left = cur.right
- cur 不是 root,cur 是 parent.right,则 parent.right = cur.right
3.要删除的是根节点,即cur.left != null && cur.right != null;
有两种简单的解决思路:
1.找到以此节点为根的左子树的最右节点,交换根节点与最右节点,再删除交换到最右节点处的根节点
2.找到右子树的最左节点,同样交换两个节点再删除
代码实现:
找到要删除节点的位置
//找到要删除的节点
public boolean remove(int val){
if(root == null){
return false;
}
Node cur = root;
//cur的后继节点,保存cur的父节点的位置
Node parent = null;
//找到节点在搜索树中的位置
while(cur != null){
if(cur.val == val){
break;
}else if(cur.val > val){
parent = cur;
cur = cur.left;
}else{
parent = cur;
cur = cur.right;
}
}
//判断节点是否存在
if(cur == null){
return false;
}
//将此节点从搜索树中删除
removeNode(parent, cur);
return true;
}
删除这个节点
//删除的过程
public void removeNode(Node parent, Node cur){
//1.cur左子树为空(包括cur为叶子节点,此时cur.right为null而已)
//2.cur右子树为空
//3.cur左右子数均不为空
if(cur.left == null){
if(cur != root){
//cur的子树应该连接在父节点的方向
if(parent.left == cur){
parent.left = cur.right;
}else{
parent.right = cur.right;
}
}else{
root = cur.right;
}
}else if(cur.right == null){
if(cur != root){
if(parent.left == cur){
parent.left = cur.left;
}else{
parent.right = cur.left;
}
}else{
root = cur.left;
}
}else{//cur左右均不为空
parent = cur;
Node next = cur.left;
//找到左子树的最右节点
while(next.right != null){
parent = next;
next = next.right;
}
//将左子树的最右节点的值赋给cur
cur.val = next.val;
//删除左子树最右节点
if(parent.left == next){
parent.left = next.left;
}else{
parent.right = next.left;
}
}
}