欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

二叉搜索树(java实现)

程序员文章站 2022-05-06 23:46:11
...

目录
  1 .插入元素思想与实现
  2 .删除元素思想与实现
  3 .完整代码

二叉搜索树定义

二叉搜索树,也称有序二叉树,排序二叉树,是指一棵空树或者具有下列性质的二叉树:

  1. 若任意节点的左子树不空,则左子树上所有结点的值均小于它的根结点的值;

  2. 若任意节点的右子树不空,则右子树上所有结点的值均大于它的根结点的值;

  3. 任意节点的左、右子树也分别为二叉查找树。

  4. 没有键值相等的节点。

构造节点:

class Node<T extends Comparable> {
    int key;
    T val;
    Node<T> left;
    Node<T> right;

    public Node() {

    }

    public Node(int key, T val) {
        this.key = key;
        this.val = val;
    }
}

二叉搜索树插入

  插入的过程是:比较当前结点,如果小于当前结点,就往左子树查找。如果大于当前结点,就往右子树查找。直到查找的子树为空树的时候,就是其插入的位置。

private void insertKV(int key, T val) {
        // 如果是空树,则插入的节点为根节点
     if (root == null) {
        root = new Node<T>(key, val);
        return;
     }
     Node cur = root;
     Node parent = root;
     boolean isLeftNode = true;
     while (cur != null) {//直到找到该插入的位置
        parent = cur;
        if (key < cur.key) { // 如果小于当前节点的值,往左子树查找
           cur = cur.left;
           isLeftNode = true;
        } else { // 如果大于当前节点的值,往右子树查找
           cur = cur.right;
           isLeftNode = false;
        }
     }
     Node newNode = new Node(key, val);
     if (isLeftNode) {
         parent.left = newNode;
     } else {
         parent.right = newNode;
   }
}

二叉搜索树的删除

删除应该属于二叉搜索树中最复杂的情况了。

有对应4种情况:

  • 删除的元素为叶子节点
      把该节点的父节点指向该节点的引用指向null就可以
  • 删除元素只有右孩子
      将该节点的右子数上移一层
  • 删除元素只有左孩子
      将该节点的左子数上移一层
  • 删除的元素既有左孩子,又有右孩子(这个情况最为复杂)
      需要在保持排序树的结构下进行上旋和下旋

1.1 先将当前结点定位到要删除的节点

 /**
 * 找到要删除的节点,并把当前结点定位到要删除的节点
*/
while (cur.key!=key){
    parent = cur;
    if(key<cur.key){ //如果删除的元素在左子树
        isLeftChild = true;//在左子树设为true
        cur = cur.left;
    }else { //如果删除的元素在右子树
        isLeftChild = false;
        cur = cur.right;
    }
    if(cur == null){//不存在该节点
        return false;
    }
}

1.2.1 删除叶子节点

if(cur.left == null&&cur.right==null){//当前结点是叶子节点的时候
    if(cur == root){ //是根节点,且根节点没有叶子节点的时候
        root = null; // 树为空树
    }
    else if(isLeftChild){// 当前结点是左孩子节点
        parent.left = null; //父节点的左孩子设为空
    }else {// 当前结点是右孩子节点
        parent.right = null;//父节点的左孩子设为空
    }
}

1.2.2 删除元素只有右孩子

else if(cur.left == null){//当前结点没有左孩子,只有右孩子
    if (cur == root){ //当前结点为根
        root = cur.right;//左子树上移
    }else if(isLeftChild){ // 当前结点是其父节点的左孩子
        parent.left = cur.right;
    }else { // 当前结点是其父节点的右孩子
        parent.right = cur.right;
    }
}

1.2.3 删除元素只有左孩子

else if(cur.right == null){//当前结点没有右孩子,只有左孩子
    if (cur == root){ //当前结点为根
        root = cur.left;//左子树上移
    }else if(isLeftChild){ // 当前结点是其父节点的左孩子
        parent.left = cur.left;
    }else { // 当前结点是其父节点的右孩子
        parent.right = cur.left;
    }
}

1.2.4 删除的元素既有左孩子,又有右孩子

else {//当前结点有左孩子,也有右孩子
    Node<T> successor = getSuccessor(cur);
    if(cur == root){
        root = successor;
    }
    else if(isLeftChild){
        parent.left = successor;
    }
    else {
        parent.right = successor;
    }
    //将删除节点的左子树接到旋好的树的左子树上
    successor.left = cur.left;
}

/**
* 将要删除节点为根节点的树
* 进行旋转操作
* @param delNode 要删除的节点
* @return 旋好的子树
*/
private Node<T> getSuccessor(Node delNode) {
    Node<T> successorParent = delNode;
    Node<T> successor = delNode;
    Node<T> cur = delNode.right;
    while(cur!=null){
        successorParent = successor;
        successor = cur;
        cur = cur.left;
    }
    if(successor!=delNode.right){
        successorParent.left = successor.right;
        successor.right = delNode.right;
    }
    return successor;
}

删除18节点,代码执行过程。经过旋转结果的到以18为根的树变成了右图,虚线是转完之后接上18的左子树

二叉搜索树(java实现)

同样是删除18节点,18的子树变复杂了,转后的子树如右

二叉搜索树(java实现)

二叉搜索树(java实现)

二叉搜索树(java实现)

看了这几张转后的图片,可以知道我旋转子树的思想。

首先看删除节点的右子树,如果为空,就把该节点的右孩子节点往上提一层。

如果右子树不为空,在看右子树的左子树,如果左子树为空,把整个右子树往上提一层。

如果右子树的左子树不为空,且左子树没有孩子节点的时候,直接把右子树的左子树提到要删除节点的位置

有左孩子的时候,像以上的循环就可以,直到左孩子为空,循环停止。

以上是我个人的实现时的理解方式。大致可以对比这几张图,就能达到理解的目的。
注意:旋转实现,只要能保证二叉搜索树的特点,定义就可以。旋转也不只是一种实现方式。

完整代码

完整代码地址:我的代码

相关标签: 二叉搜索树