数据结构 -- 二叉查找树(新增、查找、删除)JAVA代码实现

二叉查找树

特点

  • 所有节点最多拥有两个子节点,即度不大于2
  • 左子树的键值小于根的键值,右子树的键值大于根的键值
  • 二叉树的左右子树都是二叉树
  • 没有键值相同的节点
! 在这里插入图片描述

缺点

  • 只限制了节点的基本顺序,所以存在多种深度,如上图,同一组数据生成的这两种不同的树,第二种树查询效率很低,对于值 4 的查询,几乎要遍历全部的节点才能得到结果。

插入及删除原理

插入是在叶子节点上新增内容
删除分为三种情况:

  • 待删除的节点没有子节点
    如上图 ② 中的 值 4,此时删除该节点不影响二叉树的整体结构,直接删除即可

  • 待删除的节点有一个子节点
    如上图 ② 中的值 6,此时删除节点 6 ,会导致 4 与树断开链接,因此我们需要进行调整, 6 作为 8 的左子节点,因此 6 的子节点中的所有值都小于 8 ,即 6 的子节点 一定是 10 的左子节点,而 6 只有一个子节点,因此子节点树的节点顺序不需要改变,即将节点 8 的左子节点直接替换为 4 即可。右子节点同理

  • 待删除的节点有两个子节点
    在这里插入图片描述
    在这里插入图片描述

  • 先了解一下后继节点的概念,后继节点为在中序遍历中紧跟在目标节点后面一个的节点,因为中序遍历时按照左中右的顺序,因此后继节点一定是目标节点右子节点中的最小左节点。

  • 如图③节点 9,拥有两个子节点,此时删除该节点影响二叉树的整体结构,我们需要对其进行重新排序。

  • 图③的中序排序顺序为 【3】【9】【10】【14】【15】【18】【20】【30】

  • 按照中序排列获得 9 的后继节点为 10 ,因为 109 的后继节点,所以 9 的左子节点都小于 10 ,除了 10 之外的右子节点都大于 10 ,所以我们将 10 移动到 9 的位置,将节点 9 的左子节点和右子节点都赋值给 10,就可以保持二叉树的结构。

  • 但是还有一种情况,即图④,后继节点拥有一个右子节点,这个时候我们不能直接将后继节点移动到待删除的节点位置上。

  • 图④的中序排序顺序为 【3】【9】【10】【12】【14】【15】【18】【20】【30】

  • 但是同理,按照中序排列获得 9 的后继节点为 10 ,因为 109 的后继节点,所以 9 的左子节点都小于 10 ,而除 10 节点树【10】【12】之外的所有节点,都大于 10 节点树中的最大即最右节点 【12】.因此,我们需要将节点 9 的左子节点赋值给 10,将 9 的右子节点赋值给【12】,然后将 10 移动到 9 的位置即可。

Java代码实现

节点实体类,记录节点信息

  • 包含当前节点的值、左子节点信息、右子节点信息
  • 提供先序遍历、中序遍历、后序遍历方法
public class Node {
    
    

    private Integer value;
    private Node parentNode;
    private Node leftNode;
    private Node rightNode;

    public Node(){
    
    
    }

    private void setParentNode(Node node){
    
    
        this.parentNode = node;
    }

    public Node getParentNode(){
    
    
       return this.parentNode;
    }

    public Integer getValue(){
    
    
        return this.value;
    }

    public void setValue(Integer value){
    
    
        this.value = value;
    }

    public Node getLeftNode(){
    
    
        return this.leftNode;
    }

    public void setLeftNode(Node node){
    
    
		//自动设置父节点
        if(null != node ) node.setParentNode(this);
        this.leftNode = node;
    }

    public Node getRightNode(){
    
    
        return this.rightNode;
    }

    public void setRightNode(Node node){
    
    
        //自动设置父节点
        if(null != node ) node.setParentNode(this);
        this.rightNode = node;
    }

    /**
     * 前序遍历
     */
    public void DLR(){
    
    
        if(value!=null) {
    
    
            System.out.print(String.format("【%d】", value));
            if (this.leftNode != null) {
    
    
                this.leftNode.DLR();
            }
            if (this.rightNode != null) {
    
    
                this.rightNode.DLR();
            }
        }
    }

    /**
     * 中序遍历
     */
    public void LDR(){
    
    
        if(value!=null) {
    
    
            if (this.leftNode != null) {
    
    
                this.leftNode.LDR();
            }
            System.out.print(String.format("【%d】", value));
            if (this.rightNode != null) {
    
    
                this.rightNode.LDR();
            }
        }
    }

    /**
     * 后续遍历
     */
    public void LRD() {
    
    
        if (value != null) {
    
    
            if (this.leftNode != null) {
    
    
                this.leftNode.LRD();
            }
            if (this.rightNode != null) {
    
    
                this.rightNode.LRD();
            }
            System.out.print(String.format("【%d】", value));
        }
    }
}

二叉树操作类

public class BstTree {
    
    

    private Node root;

    public Node getTree(){
    
    
        return this.root;
    }

    /**
     * 插入元素
     * @param i
     */
    public void insert(int i){
    
    

        Node newNode = new Node();
        newNode.setValue(i);

        if(root==null){
    
    
            //没有根结点时 将当前节点作为根节点
            root=newNode;
        }else{
    
    
            Node currentNode = root;
            Node parentNode;

            while (null != currentNode) {
    
    
                // 当前节点视为父节点
                parentNode = currentNode;
                if (i > currentNode.getValue()) {
    
    
                    // 大于父节点的值 且父节点没有右子节点 则作为右节点插入
                    currentNode = currentNode.getRightNode();
                    if (null == currentNode) {
    
    
                        parentNode.setRightNode(newNode);
                    }
                } else if(i < currentNode.getValue()) {
    
    
                    // 小于父节点的值 且父节点没有左子节点 则作为左节点插入
                    currentNode = currentNode.getLeftNode();
                    if (null == currentNode) {
    
    
                        parentNode.setLeftNode(newNode);
                    }
                }else{
    
    
                    //值已存在 则跳出
                    System.out.println(String.format("值【%d】已存在",i));
                    return;
                }
            }
        }
    }

    /**
     * 查找元素节点
     * @param key
     * @return
     */
    public Node find(int key) {
    
    
        Node currentNode = root;
        if (null != currentNode) {
    
    
            // 判断是否是当前节点
            while (key != currentNode.getValue()) {
    
    
                //值大于当前节点 向右遍历
                if (key > currentNode.getValue()) {
    
    
                    currentNode = currentNode.getRightNode();
                } else {
    
    
                    //值小于当前节点 向左遍历
                    currentNode = currentNode.getLeftNode();
                }
                if (null == currentNode) {
    
    
                    return null;
                }
            }
        }
        return currentNode;
    }

    /**
     * 查找 node 的前驱节点
     * @param node
     * @return
     */
    public Node precursor(Node node) {
    
    
        //前驱节点即 左子树的最大节点
        Node maxNode = null;
        if(node.getLeftNode()!=null){
    
    
            maxNode=node.getLeftNode();
            while(maxNode!=null){
    
    
                Node currentChild = maxNode.getRightNode();
                if(currentChild!=null){
    
    
                    maxNode = maxNode.getLeftNode();
                }else{
    
    
                    break;
                }
            }
        }
        return maxNode;
    }

    /**
     * 查找 node 的后继节点
     * @param node
     * @return
     */
    public Node successor(Node node) {
    
    
        //后继节点 即 右节点的最小值
        Node minNode = null;
        if(node.getRightNode()!=null){
    
    
            minNode=node.getRightNode();
            while(minNode!=null){
    
    
                Node currentChild = minNode.getLeftNode();
                if(currentChild!=null){
    
    
                    minNode = minNode.getLeftNode();
                }else{
    
    
                    break;
                }
            }
        }
        return minNode;
    }

    /**
     * 用新的节点替换当前节点
     * @param oldNode
     * @param newNode
     */
    private void updateNode(Node oldNode,Node newNode){
    
    
        if (oldNode.getParentNode().getLeftNode() == oldNode){
    
    
            oldNode.getParentNode().setLeftNode(newNode);
        }else if (oldNode.getParentNode().getRightNode() == oldNode){
    
    
            oldNode.getParentNode().setRightNode(newNode);
        }
    }

    /**
     * 删除节点 分三种情况
     * 叶子节点 直接删除
     * 有一个子节点  将子节点的父节点设置为被删除节点的父节点
     * 左右节点都存在时 可以先找到需要删除的节点的后继节点或前驱节点替换当前节点
     * @param key
     */
    public void delete(int key) {
    
    

        Node deleteNode = find(key);

        if(deleteNode!=null){
    
    
            if(deleteNode.getLeftNode()!=null && deleteNode.getRightNode()!=null){
    
    
                
                //两个子节点,查找后继节点替换当前节点
                Node backContinueNode = successor(deleteNode);

                //将后继节点 替换为 NULL
                this.updateNode(backContinueNode,null);

                //左子节点赋值
                backContinueNode.setLeftNode(deleteNode.getLeftNode());
                //后继节点有右子节点
                Node rightNode = backContinueNode.getRightNode();
                if(rightNode!=null){
    
    
                    //获得最后一个右子节点 赋值
                    while(rightNode!=null){
    
    
                        if(rightNode.getRightNode()!=null){
    
    
                            rightNode=rightNode.getRightNode();
                        }else{
    
    
                            //最后一个右子节点
                            rightNode.setRightNode(deleteNode.getRightNode());
                            break;
                        }
                    }
                }else{
    
    
                    //将待删除节点的子节点内容赋值给 后继节点
                    backContinueNode.setRightNode(deleteNode.getRightNode());
                }

                //将待删除节点 替换为 后继节点
                //需要删除的节点没有被引用的地方,会自动回收
                this.updateNode(deleteNode,backContinueNode);
            }else if(deleteNode.getLeftNode()!=null){
    
    
                
                //只有左子节点 将左子节点和父节点直接相连
                this.updateNode(deleteNode,deleteNode.getLeftNode());
            }else if(deleteNode.getRightNode()!=null){
    
    
                
                //只有右子节点 将右子节点和父节点直接相连
                this.updateNode(deleteNode,deleteNode.getRightNode());
            }else{
    
    
                
                //没有子节点 
                this.updateNode(deleteNode,null);
            }
        }else{
    
    
            throw new RuntimeException("没有该节点");
        }
    }
}

猜你喜欢

转载自blog.csdn.net/qq_40096897/article/details/121476781