• 数据结构--二叉搜索树


    对于数组,查找速度快,而添加和删除速度慢;链表,查找速度慢,而添加和删除速度快;树具备查找、增删速度快的特点

    树的常用术语

    • 路径:从一个节点到另一个节点,所经过的节点的顺序排列称为'路径’
    • :树顶端的节点称为根。一棵树只有一个根,如果要把一个节点和边的集合称为树,那么从根到其他任何一个节点都必须有且只有一条路径
    • 父节点:若一个节点含有子节点,则这个节点称为其子节点的父节点
    • 子节点:一个节点含有的子树的根节点称为该节点的子节点
    • 兄弟节点:具有相同父节点的节点互称为兄弟节点
    • 叶节点:没有子节点的节点称为叶节点,也叫叶子节点
    • 子树:每个节点都可以作为子树的根,它和它所有的子节点、子节点的子节点等都包含在子树中
    • 节点的层次:从根开始定义,根为第一层,根的子节点为第二层,以此类推
    • 深度:对于任意节点n,n的深度为从根到n的唯一路径长,根的深度为0
    • 高度:对于任意节点n,n的高度为从n到一片树叶的最长路径长,所有树叶的高度为0

    二叉树

    树的每个节点最多只能有两个子节点

    二叉搜索树

    若它的左子树不空,则左子树上所有结点的值均小于它的根结点的值; 若它的右子树不空,则右子树上所有结点的值均大于它的根结点的值; 它的左、右子树也分别为二叉排序树

    二叉树的节点类

    public class Node {
        private int data;//节点保存数据
        private Node leftChild;//左节点
        private Node rightChild;//右节点
    
    
        public Node(int data) {
            this.data = data;
        }
    
        public void display(){
            System.out.println(data);
        }
    
        public int getData() {
            return data;
        }
    
        public void setData(int data) {
            this.data = data;
        }
    
        public Node getLeftChild() {
            return leftChild;
        }
    
        public void setLeftChild(Node leftChild) {
            this.leftChild = leftChild;
        }
    
        public Node getRightChild() {
            return rightChild;
        }
    
        public void setRightChild(Node rightChild) {
            this.rightChild = rightChild;
        }
    }
    

    二叉树方法

    public interface Tree {
        /**
         * 查找节点
         * @param data
         * @return
         */
        Node find(int data);
    
        /**
         * 插入节点
         * @param data
         * @return
         */
        boolean insert(int data);
    
        /**
         * 删除节点
         * @param key
         * @return
         */
        boolean delete(int key);
    
        /**
         * 中序遍历  左子树-》根节点-》右子树
         * @param current
         */
        void inOrder(Node current);
    
        /**
         * 前序遍历:根节点-》左子树-》右子树
         * @param current
         */
        void preOrder(Node current);
    
        /**
         * 后序遍历:左子树-》右子树-》根节点
         * @param current
         */
        void postOrder(Node current);
    
        /**
         * 查找最大值节点
         * @return
         */
        Node findMax();
    
        /**
         * 查找最小值节点
         * @return
         */
        Node findMin();
    }
    

    二叉搜索树实现

    查找方法

    查找时候,从根节点开始遍历

    1. 需要查找的值比当前节点值大,搜索右子树
    2. 需要查找的值等于当前节点的值,停止搜索,返回当前节点
    3. 需要查找的值比当前节点值小,搜索左子树
    public Node find(int key) {
        Node current = root;
        if (current == null) return null;
        while (current != null){
            if (current.getData()>key){
                current = current.getLeftChild();
            }else if (current.getData() < key){
                current =  current.getRightChild();
            }else return current;
        }
        return null;
    }
    

    插入方法

    插入值时候,先确定需要插入的位置。先与根节点比较,如果值大于根节点的值,则与右子树比较,反之亦然

    public boolean insert(int data) {
        Node newNode = new Node(data);
        if (root == null){
            root = newNode;
            return true;
        }else {
            Node current = root;
            Node parentNode = null;
            while (current != null){
                parentNode = current;
                if (current.getData() > data){//如果插入值小于当前节点的值
                    current = current.getLeftChild();//获取当前节点的左节点
                    if (current == null){//如果为空,当前值插入该位置
                        parentNode.setLeftChild(newNode);
                        return true;
                    }
                }else {
                    current = current.getRightChild();
                    if (current == null){
                        parentNode.setRightChild(newNode);
                        return true;
                    }
                }
            }
        }
        return false;
    }
    

    遍历方法

    遍历树是按照一定的顺序访问树的每一个节点

    1. 中序遍历:左子树-》根节点-》右子树
    2. 前序遍历:根节点-》左子树-》右子树
    3. 后序遍历:左子树-》右子树-》根节点

    树

    中序遍历:DBEAFCG
    前序遍历:ABDECFG
    后序遍历:DEBFGCA
    
    @Override
    public void inOrder(Node current) {
        if (current != null){
            inOrder(current.getLeftChild());
            System.out.print(current.getData());
            inOrder(current.getRightChild());
        }
    }
    
    @Override
    public void preOrder(Node current) {
        if (current!= null){
            System.out.print(current.getData());
            preOrder(current.getLeftChild());
            preOrder(current.getRightChild());
        }
    }
    
    @Override
    public void postOrder(Node current) {
        if (current!= null){
            postOrder(current.getLeftChild());
            postOrder(current.getRightChild());
            System.out.print(current.getData());
        }
    }
    

    查找最大最小值方法

    最大最小值,就是一直查询右/左节点直到右/左节点不存在,就是最大/小值

    @Override
    public Node findMax() {
        Node current = root;
        Node maxNode = root;
        while (current != null){
            maxNode = current;
            current = current.getRightChild();
        }
        return maxNode;
    }
    
    @Override
    public Node findMin() {
        Node current = root;
        Node minNode = root;
        while (current != null){
            minNode = current;
            current = current.getLeftChild();
        }
        return minNode;
    }
    

    删除方法

    删除时候分为三种情况

        1. 删除节点不包含子节点
        2. 删除节点包含一个子节点
        3. 删除节点包含两个子节点
    
    • 删除节点不包含子节点

    只需改变删除节点的父节点引用该节点的值,将其指向null。要删除的节点不再引用,等待垃圾回收器回收即可

    • 删除节点包含一个子节点

    删除节点的父节点指向删除节点的子节点

    • 删除节点包含两个子节点

    对于删除节点存在两个子节点时候,删除后,两个子节点需要重新构成一个新的子树,删除节点的父节点指向新的子树,
    子树的根节点为删除节点的后继节点。后继节点也就是比删除节点大的最小节点
    后继节点

    确认后续节点后,又需要考虑后续节点时候是否为删除节点的位置

    • 后续节点是删除节点的右子节点
      后继节点
    • 后续节点不是删除节点的右子节点,则必定为删除节点的右子节点的左子节点
      后继节点
    public boolean delete(int key) {
        if (root == null) return false;//树为空,返回false
        Node current = root;
        Node parent = root;
        boolean isLeftChild = false;//用于判断需要删除的节点是其父节点的左/右子节点
        while (current.getData() != key){
            parent = current;
            if (key > current.getData()){
                isLeftChild = false;
                current = current.getRightChild();
            }else {
                isLeftChild = true;
                current = current.getLeftChild();
            }
            if (current == null)return false;//未找到,返回false
        }
        if (current.getRightChild() == null &&
            current.getLeftChild() == null){//需要删除的节点不包含子节点
    
            if (current == root){
                root = null;
            }else if (isLeftChild){
                parent.setLeftChild(null);
            }else parent.setRightChild(null);
    
        }else if (current.getRightChild() == null &&
                  current.getLeftChild() != null){//需要删除的节点包含一个节点,且为左节点
    
            if (current == root){
                root = current.getLeftChild();
            }else if (isLeftChild){
                parent.setLeftChild(current.getLeftChild());
            }else parent.setRightChild(current.getLeftChild());
    
        }else if (current.getRightChild() != null &&
                  current.getLeftChild() == null){//需要删除的节点包含一个节点,且为右节点
    
            if (current == root){
                root = current.getRightChild();
            }else if (isLeftChild){
                parent.setLeftChild(current.getRightChild());
            }else {
                parent.setRightChild(current.getRightChild());
            }
    
        }else {//需要删除的节点包含两个节点
            Node successor = geSuccessor(current);//获得后继节点
            if (current == root){
                root = successor;
            }else if (isLeftChild){
                parent.setLeftChild(successor);
            }else parent.setRightChild(successor);
            successor.setLeftChild(current.getLeftChild());
        }
    
        return true;
    }
    
    private Node geSuccessor(Node delNode) {
        Node successorParent = delNode;
        Node successor = delNode;
        Node current = delNode.getRightChild();
        while (current != null){
            successorParent = successor;
            successor = current;
            current = current.getLeftChild();
        }
        if (successor != delNode.getRightChild()){//后继节点不是需要删除节点的右节点,调整结构
            successorParent.setLeftChild(successor.getRightChild());
            successor.setRightChild(delNode.getRightChild());
        }
        return successor;
    }
    

    完整的二叉查找树代码

    https://github.com/Jzedy/Z-books/blob/master/src/main/test/java/treeStart/BinaryTree.java

  • 相关阅读:
    weblogic 扩展集群里受管节点
    docker搭建weblogic环境
    Docker 快速删除所有容器
    转载的mysql 相关
    mysql字符集修改步骤
    ORA-12705: Cannot access NLS data files or invalid environment
    Authentication for user weblogic denied
    ONS 禁用
    Java-并发-什么是CAS机制?
    862. 和至少为 K 的最短子数组-前缀和/数组/滑动窗口-困难
  • 原文地址:https://www.cnblogs.com/JzedyBlogs/p/10428515.html
Copyright © 2020-2023  润新知