一、BST(Binary Search Tree):二叉搜索树,将数据的增加、删除、查询的时间复杂度都达到O(log2 n)
①所有的非叶子节点至多拥有两个节点(右孩子>父节点的值>左孩子)
②所有的节点除过存储自己的数据之外,还包括左孩子和右孩子的信息
③每一层节点的个数为 2^n (根节点为0层)–>树的深度为(log2 n)
如下图所示,对一组数进行BST排列:(53,28,82,12,35,69,87,18,47,74,95)
二、代码实现BST树:
①因为每一个节点的类型为
class BSTnode<T extends Comparable<T>>{ //因为要进行比较所以需要对泛型规定上界
private T data; //数据域
private BSTnode<T> left; //存放左孩子
private BSTnode<T> right; //存放右孩子
……
}
②、生成二叉搜索树(数据的插入insert (T data) ):
1>肯定从根节点(root)开始生成,即第一个插入的数为根节点。
public void insert(T data){
//判断root是否为空,生成根节点
if (root==null){
this.root=new BSTnode<>(data,null,null);
return;
}
2> 之后插入的数与其比较大于则继续往右孩子查询,小于则继续往左孩子查询
node指向根节点进行遍历,重复步骤 2找到为空的合适插入的位置,parent为要插入位置的父节点
//如果root不为空,从根节点找到合适位置
BSTnode<T> node=root;
BSTnode<T> parent=null;
while (node!=null) { //找到合适的位置且为空跳出循环
if (node.getData().compareTo(data) > 0) { //如果插入元素大于根节点
parent=node;
node = node.getLeft();
}
else if (node.getData().compareTo(data) < 0){
parent=node;
node = node.getRight();
}
else { //不允许插入重复的元素
return;
}
}
3> 重复步骤2直至找到为空的位置,生成新节点并将新节点放置到该位置。
此时已经找到要插入位置的父节点,还需要判断是放置于
(1)父节点的左节点(val<父节点.data)
(2)父节点的右节点(val>父节点.data)
//3.生成新节点,把节点的地址,写入父节点相应的地址域
if (parent.getData().compareTo(data) >0){
parent.setLeft(new BSTnode<>(data,null,null));
}
else if (parent.getData().compareTo(data) < 0){
parent.setRight(new BSTnode<>(data,null,null));
}
③、数据的删除(remove (T val) )
思路分析:
(若数据存在)删除的数据分为三种情况:
1.删除有两个孩子的节点
找到删除节点的前驱节点或后继节点,用前驱节点或后继节点把
当前删除节点的值覆盖掉。
然后直接删除前驱节点或者后继节点---->转换成删除一个节点或删除叶子节点
①前驱节点:待删除节点左子树中,值最大的节点
②后继节点:待删除节点右子树中,值最小的节点`
//1.先找到待删除节点
BSTnode<T> cur=root;
BSTnode<T> parent=null;
while (cur!=null){
if (cur.getData().compareTo(val)>0){ //删除元素小于当前节点
parent=cur;
cur=cur.getLeft();
}
else if (cur.getData().compareTo(val)<0){ //删除元素da于当前节点
parent=cur;
cur=cur.getRight();
}
else{
break;
}
}
此时cur为待删除节点,parent为cur的父节点(相当于上图53,因为后边要进行重写)
if (cur == null) { //表示BST树中没有值为val的节点
return;
}
//2.判断节点是否有两个孩子,若有,用前驱代替,直接删除前驱
if (cur.getRight()!=null&&cur.getLeft()!=null){ //当前节点有两个孩子
//找到当前节点的前驱节点,从当前位置往下找
BSTnode<T> old=cur; //用old保存待删除节点
parent=cur;
cur=cur.getLeft(); //找待删除节点的前驱节点
while (cur.getRight()!=null){
parent=cur;
cur=cur.getRight();
}
//此时cur指向前驱节点
old.setData(cur.getData()); //将前驱节点的值覆盖要删除的节点值
}
//3.删除有一个孩子的节点或者没有孩子的节点(看做有一个孩子,为null),此时cur为前驱节点
BSTnode<T> child=cur.getLeft(); //先将child指向左孩子
if (child==null){ //待删除节点没有左孩子或者没有孩子
child=cur.getRight();
}
if (parent==null){ //删除的为根节点
this.root=child;
}
//删除的节点有左孩子或者没有孩子,为下图删除28的情况,前驱节点为47,将47放置到28 的位置,则需要删除节点47
else {
if (parent.getLeft()==cur){
parent.setLeft(child);
}
else {
parent.setRight(child);
}
}
}
③、数据的查找:
若待查找数据小于根节点,则在左子树查找
否则,在右子树查找
重复上述操作,直到查找到返回true;
若查找到叶子节点还没有找到,返回false;
public boolean non_query(T val){
BSTnode<T> cur=root;
BSTnode<T> parent=null;
while (cur!=null){
if (cur.getData().compareTo(val)>0){ //删除元素小于当前节点
cur=cur.getLeft();
}
else if (cur.getData().compareTo(val)<0){ //删除元素da于当前节点
cur=cur.getRight();
}
else {
return true;
}
}
return false;
}