Java 数据结构篇-实现二叉搜索树的核心方法

🔥博客主页: 【小扳_-CSDN博客】
❤感谢大家点赞👍收藏⭐评论✍
 

文章目录

        1.0 二叉搜索树的概述

        2.0 二叉搜索树的成员变量及其构造方法

        3.0 实现二叉树的核心接口

        3.1 实现二叉搜索树 - 获取值 get(int key)

        3.2 实现二叉搜索树 - 获取最小的关键字 min(BinaryNode node)

        3.3 实现二叉搜索树 - 获取最大的关键字 max(BinaryNode node)

        3.4 实现二叉搜索树 - 增、更新 put( int key, Object value)

        3.5 实现二叉搜索树 - 查找关键字的后驱节点 successor(int key)

        3.6 实现二叉搜索树 - 查找关键字的前驱节点 predecessor(int key)

        3.7 实现二叉搜索树 - 删除关键字节点 delete(int key)

        3.8 实现二叉搜索树 - 查找范围小于关键字的节点值 less(int key)

        3.9 实现二叉搜索树 - 查找范围大于关键字的节点值 greater(int key)

        4.0 实现二叉搜索树 - 查找范围大于 k1 且小于 k2 关键字的节点值 between(int k1, int k2)

        5.0 实现二叉搜索树核心方法的完整代码


        1.0 二叉搜索树的概述

        二叉搜索树是一种数据结构,用于存储数据并支持快速的插入、删除和搜索操作。它是一种树形结构。

        它具有以下特点:

                - 每个节点最多有两个子节点,分别称为左子节点和右子节点。

                - 对于每个节点,其左子节点的值小于该节点的值,右子节点的值大于该节点的值。

                - 中序遍历二叉搜索树可以得到有序的元素序列。

        由于其特性,二叉搜索树在插入、删除和搜索操作上具有较高的效率。在平均情况下,这些操作的时间复杂度为 O(log n),其中 n 为树中节点的数量。然而,如果树的结构不平衡,最坏情况下这些操作的时间复杂度可能会达到 O(n)。由于其高效的搜索特性,二叉搜索树常被用于实现关联数组和集合等数据结构。然而,为了避免树的结构不平衡导致性能下降,人们也发展了平衡二叉搜索树(如红黑树、AVL树)等变种。

        2.0 二叉搜索树的成员变量及其构造方法

        外部类成员变量有:根节点节点类(内部类)

        外部类构造方法:默认的构造方法,对外公开二叉搜索树的核心方法

        节点类的成员变量有:

                - key 关键字:相对比一般的二叉树,二叉搜索树可以明显提高增删查改的效率原因在于关键字,可以根据比较两个关键字的大小进行操作。

                - value 值:作用则为存放值。

                - left :链接左节点。

                - right:链接右节点。

        节点类的构造方法:

                带两个参数的构造方法:参数为 key 、value 

                带四个参数的构造方法:参数为 key 、value 、left 、right

代码如下:

public class BinaryTree {

    BinaryNode root = null;
    static class BinaryNode {
        int key;
        Object value;
        BinaryNode left;
        BinaryNode right;

        public BinaryNode(int kty, Object value) {
            this.key = kty;
            this.value = value;
        }

        public BinaryNode(int key, Object value, BinaryNode left, BinaryNode right) {
            this.key = key;
            this.value = value;
            this.left = left;
            this.right = right;
        }
    }

}

        补充二叉搜索树在增、删、查、改的效率高的原因:

        二叉搜索树的高效性与其关键字的特性密切相关。二叉搜索树的关键特性是,对于每个节点,其左子节点的值小于该节点的值,右子节点的值大于该节点的值。这种特性使得在二叉搜索树中进行搜索、插入和删除操作时,可以通过比较关键字的大小来快速定位目标节点,从而实现高效的操作。在平均情况下,这些操作的时间复杂度为 O(log n),其中 n 为树中节点的数量。因此,关键字的有序性是二叉搜索树能够实现高效操作的关键原因之一。

        3.0 实现二叉树的核心接口

​
public interface BinarySearchTreeInterface {

    /**
     *查找 key 对应的 value
     */
    Object get(int key);

    /**
     * 查找最小关键字对应值
     */
    Object min();

    /**
     * 查找最大关键字对应值
     */
    Object max();

    /**
     * 存储关键字与对应值
     */
    void put(int key, Object value);

    /**
     * 查找关键字的后驱
     */
    Object successor(int key);

    /**
     * 查找关键字的前驱
     */
    Object predecessor(int key);

    /**
     * 根据关键字删除
     */
    Object delete(int key);
}

​

        3.1 实现二叉搜索树 - 获取值 get(int key)

        实现思路为:从根节点开始,先判断当前的节点 p.key 与 key 进行比较,若 p.key > key,则向左子树下潜 p = p.left ;若 p.key < key ,则向右子树下潜 p = p.right ;若 p.key == key ,则找到到了关键字,返回该节点的值 p.value 。按这样的规则一直循环下去,直到 p == null 退出循环,则说明没有找到对应的节点,则返回 null 。

代码如下:

    @Override
    public Object get(int key) {
        if (root == null) {
            return null;
        }
        BinaryNode p = root;
        while(p != null) {
            if (p.key > key) {
                p = p.left;
            }else if (p.key < key) {
                p = p.right;
            }else {
                return p.value;
            }
        }
        return null;
    }

        若 root 为 null ,则不需要再进行下去了,直接结束。

        3.2 实现二叉搜索树 - 获取最小的关键字 min(BinaryNode node)

        实现思路:在某一个树中,需要得到最小的关键字,由根据数据结构的特点,最小的关键字在数的最左边,简单来说:一直向左子树遍历下去,直到 p.left == null 时,则该 p 节点就是最小的关键字了。然后找到了最小的节点,返回该节点的值即可。

代码如下:

非递归实现:

    @Override
    public Object min() {
        if (root == null) {
            return null;
        }
        BinaryNode p = root;
        while(p.left != null) {
            p = p.left;
        }
        return p.value;
    }
    //重载了一个方法,带参数的方法。
    public Object min(BinaryNode node) {
        if (node == null) {
            return null;
        }
        BinaryNode p = node;
        while (p.left != null) {
            p = p.left;
        }
        return p.value;
    }

递归实现:

    //使用递归实现找最小关键字
    public Object minRecursion() {
        return doMin(root);
    }
    private Object doMin(BinaryNode node) {
        if (node == null) {
            return null;
        }
        if (node.left == null) {
            return node.value;
        }
        return doMin(node.left);
    }

        

        3.3 实现二叉搜索树 - 获取最大的关键字 max(BinaryNode node)

        实现思路为:在某一个树中,需要得到最大的关键字,由根据数据结构的特点,最大的关键字在数的最右边,简单来说:一直向右子树遍历下去,直到 p.right == null 时,则该 p 节点就是最大的关键字了。然后找到了最大的节点,返回该节点的值即可。

代码如下:

非递归实现:

    @Override
    public Object max() {
        if (root == null) {
            return null;
        }
        BinaryNode p = root;
        while(p.right != null) {
            p = p.right;
        }
        return p.value;
    }
    //重载了一个带参数的方法
    public Object max(BinaryNode node) {
        if (node == null) {
            return null;
        }
        BinaryNode p = node;
        while (p.right != null) {
            p = p.right;
        }
        return p.value;
    }

递归实现:

    //使用递归实现找最大关键字
    public Object maxRecursion() {
        return doMax(root);
    }
    private Object doMax(BinaryNode node) {
        if (node == null) {
            return null;
        }
        if (node.right == null) {
            return node.value;
        }
        return doMax(node.right);
    }

        3.4 实现二叉搜索树 - 增、更新 put( int key, Object value)

        实现思路为:在二叉搜索树中先试着查找是否存在与 key 对应的节点 p.key 。若找到了,则为更新该值 p.value = value 即可。若找不到,则需要新增该关键字节点

        具体来分析如何新增关键字,先定义 BinaryNode parent 、 BinaryNode p,p 指针在去比较 key 之前,先让 parent 指向 p 。最后循环结束后, p == null ,对于 parent 来说,此时正指着 p 节点的双亲节点。 接着创建一个新的节点,BinaryNode newNode = new BinaryNode(key, value) ,则此时还需要考虑的是,该新的节点该连接到 parent 的左孩子还是右孩子 ?需要比较 parent.key 与 newNode.key 的大小即可,若 parent.key > newNode.key,则链接到 parent.left 处;若 prent.key < newNode.key ,则连接到 parent.right 处。

代码如下:

    @Override
    public void put(int key, Object value) {
        if (root == null) {
            root = new BinaryNode(key,value);
            return;
        }
        BinaryNode p = root;
        BinaryNode parent = null;
        while (p != null) {
            parent = p;
            if (p.key > key) {
                p = p.left;
            } else if (p.key < key) {
                p = p.right;
            }else {
                p.value = value;
                return;
            }
        }

        //该树没有该关键字,因此需要新建节点对象
        BinaryNode newNode = new BinaryNode(key,value);
        if (newNode.key < parent.key) {
            parent.left = newNode;
        }else {
            parent.right = newNode;
        }

    }

        3.5 实现二叉搜索树 - 查找关键字的后驱节点 successor(int key)

        具体实现思路为:先遍历找到该关键字的节点,若找不到,则返回 null ;若找到了,判断以下的两种情况,第一种情况:该节点有右子树,则该关键字的后驱为右子树的最小关键字;第二种情况:该节点没有右子树,则该关键字的后驱为从右向左而来的祖宗节点。最后返回该后驱节点的值 

代码如下:

    @Override
    public Object successor(int key) {
        if (root == null) {
            return null;
        }
        //先找到该关键字节点
        BinaryNode p = root;
        BinaryNode sParent = null;
        while (p != null) {
            if (p.key > key) {
                sParent = p;
                p = p.left;
            } else if (p.key < key) {
                p = p.right;
            }else {
                break;
            }
        }
        //没有找到关键字的情况
        if (p == null) {
            return null;
        }

        //情况一:该节点存在右子树,则该后继为右子树的最小关键字
        if (p.right != null) {
            return min(p.right);
        }

        //情况二:该节点不存在右子树,那么该后继就需要到祖宗从右向左的节点
        if (sParent == null) {
            //可能不存在后继节点,比如最大关键字的节点就没有后继节点了
            return null;
        }
        return sParent.value;
    }

        3.6 实现二叉搜索树 - 查找关键字的前驱节点 predecessor(int key)

        具体实现思路为:先对该二叉树进行遍历寻找 key 的节点,若遍历结束还没找到,则返回 null ;若找到了,需要判断以下两种情况:

        第一种情况:该节点有左子树,则该前驱节点为该左子树的最大关键字节点。

        第二种情况:该节点没有左子树,则该前驱节点为从左向右而来的祖宗节点。

        最后返回该前驱节点的值。

代码如下:

    @Override
    public Object predecessor(int key) {
        if (root == null) {
            return null;
        }
        BinaryNode p = root;
        BinaryNode sParent = null;
        while (p != null) {
            if (p.key > key) {
                p = p.left;
            } else if (p.key < key) {
                sParent = p;
                p = p.right;
            }else {
                break;
            }
        }
        if (p == null) {
            return null;
        }
        //情况一:存在左子树,则该前任就为左子树的最大关键字节点
        if (p.left != null) {
            return max(p.left);
        }
        //情况二:不存在左子树,则该前任为从祖宗自左向右而来的节点
        if (sParent == null) {
            return null;
        }
        return sParent.value;
    }

        3.7 实现二叉搜索树 - 删除关键字节点 delete(int key)

        具体实现思路为:先遍历二叉树,查找该关键字节点。若遍历结束了还没有找到,则返回 null ;若找到了,则需要以下四种情况:

        第一种情况:找到该删除的节点只有左子树。则直接让该左子树 "托付" 给删除节点的双亲节点,这就删除了该节点了。至于左子树是链接到双亲节点的左边还有右边这个问题,根据该数据结构的特点,由该删除节点来决定。若删除的节点之前是链接该双亲节点的左边,则左子树也是链接到该双亲节点的左边;若删除的节点之前是链接该双亲节点的右边,则左子树也是链接到该双亲节点的右边。

        第二种情况:找到该删除的节点只有右子树。则直接让该右子树 "托付" 给删除节点的双亲节点,这就删除了该节点了。至于右子树是链接到双亲节点的左边还有右边这个问题,根据该数据结构的特点,由该删除节点来决定。若删除的节点之前是链接该双亲节点的左边,则右子树也是链接到该双亲节点的左边;若删除的节点之前是链接该双亲节点的右边,则右子树也是链接到该双亲节点的右边。

        第三种情况:找到该删除节点都没有左右子树。该情况可以归并到以上两种情况的任意一种处理均可。

        第四种情况:找到该删除节点都有左右子树。分两步:第一步,先找后继节点来替换删除节点,找该后继节点直接到删除节点的右子树中找最小的关键字节点即可。第二步,需要先将后继节点的右子树处理好,需要将该右子树交给替换节点的双亲节点链接。还需要判断两种情况:第一种情况,若删除节点与替换节点是紧挨着的,对替换节点的右子树无需要求,只对左子树重新赋值;若删除节点与替换节点不是紧挨着的关系,对替换节点的左右子树都要重新赋值。

代码如下:

    @Override
    public Object delete(int key) {
        if (root == null) {
            return null;
        }
        BinaryNode p = root;
        BinaryNode parent = null;
        while (p != null) {
            if (p.key > key) {
                parent = p;
                p = p.left;
            } else if (p.key < key) {
                parent = p;
                p = p.right;
            }else {
                break;
            }
        }
        //没有找到该关键字的节点
        if (p == null) {
            return null;
        }

        //情况一、二、三:只有左子树或者右子树或者都没有
        if (p.right == null) {
            shift(parent,p,p.left);
        } else if (p.left == null) {
            shift(parent,p,p.right);
        }else {
            //情况四:有左右子树
            //替换节点采用删除节点的后继节点
            //先看被删的节点与替换的节点是否为紧挨在一起
            BinaryNode s = p.right;
            BinaryNode sParent = p;
            while (s.left != null) {
                sParent = s;
                s = s.left;
            }
            if (sParent != p) {
                //说明没有紧挨在一起,则需要将替换节点的右子树进行处理
                shift(sParent,s,s.right);
                s.right = p.right;
            }
            shift(parent,p,s);
            s.left = p.left;
        }

        return p.value;
    }
    private void shift(BinaryNode parent, BinaryNode delete, BinaryNode next) {
        if (parent == null) {
            root = next;
        } else if (parent.left == delete) {
            parent.left = next;
        }else if (parent.right == delete){
            parent.right = next;
        }
    }

        为了方便,将删除节点与替换节点之间的替换操作单独成一个方法出来。

        递归实现删除关键字 key 节点,同理,也是细分为以上描述的四种情况。

代码如下:

    //使用递归实现删除关键字节点
    public BinaryNode deleteRecursion(BinaryNode node , int key) {
        if (node == null) {
            return null;
        }
        if (node.key > key) {
            node.left = deleteRecursion(node.left,key);
            return node;
        } else if (node.key < key) {
            node.right = deleteRecursion(node.right,key);
            return node;
        }else {
            if (node.right == null) {
                return node.left;
            } else if (node.left == null) {
                return node.right;
            }else {
                BinaryNode s = node.right;
                while (s.left != null) {
                    s = s.left;
                }

                s.right = deleteRecursion(node.right,s.key);
                s.left = node.left;
                return s;
            }

        }
    }

        3.8 实现二叉搜索树 - 查找范围小于关键字的节点值 less(int key)

        具体实现思路为:利用中序遍历,来遍历每一个节点的 key ,若小于 key 的节点,直接放到数组容器中;若大于 key 的,可以直接退出循环。最后返回该数组容器即可

代码如下:

    //找 < key 的所有 value
    public List<Object> less(int key) {
        if (root == null) {
            return null;
        }
        ArrayList<Object> result = new ArrayList<>();
        BinaryNode p = root;
        Stack<BinaryNode> stack = new Stack<>();
        while (p != null || !stack.isEmpty()) {
            if (p != null) {
                stack.push(p);
                p = p.left;
            }else {
                BinaryNode pop = stack.pop();
                if (pop.key < key) {
                    result.add(pop.value);
                }else {
                    break;
                }
                p = pop.right;
            }
        }
        return result;
    }

        3.9 实现二叉搜索树 - 查找范围大于关键字的节点值 greater(int key)

        具体实现思路:利用中序遍历,来遍历每一个节点的 key ,若大于 key 的节点,直接放到数组容器中。

代码如下:

    //找 > key 的所有 value
    public List<Object> greater(int key) {
        if (root == null) {
            return null;
        }
        ArrayList<Object> result = new ArrayList<>();
        Stack<BinaryNode> stack = new Stack<>();
        BinaryNode p = root;
        while (p != null || !stack.isEmpty()) {
            if (p != null) {
                stack.push(p);
                p = p.left;
            }else {
                BinaryNode pop = stack.pop();
                if (pop.key > key) {
                    result.add(pop.value);
                }
                p = pop.right;
            }
        }
        return result;
    }

该方法的改进:遍历方向进行调整,先从右子树开始,再访问根节点,最后才到左子树。因此只要小于 key 的关键字节点,直接退出循环

代码如下:

    //改进思路:遍历方向进行调整,先从右子树开始,再访问根节点,最后才到左子树
    public List<Object> greater1(int key) {
        if (root == null) {
            return null;
        }
        ArrayList<Object> result = new ArrayList<>();
        Stack<BinaryNode> stack = new Stack<>();
        BinaryNode p = root;
        while (p != null || !stack.isEmpty()) {
            if (p != null ) {
                stack.push(p);
                p = p.right;
            }else {
                BinaryNode pop = stack.pop();
                if (pop.key > key) {
                    result.add(pop.value);
                }else {
                    break;
                }
                p = pop.left;
            }
        }
        return result;
    }

        4.0 实现二叉搜索树 - 查找范围大于 k1 且小于 k2 关键字的节点值 between(int k1, int k2)

        实现思路跟以上的思路没有什么区别,唯一需要注意的是,当前节点的 key > k2 则可以退出循环了。

代码如下:

//找到 >= k1 且 =< k2 的所有value
    public List<Object> between(int k1, int k2) {
        if (root == null) {
            return null;
        }
        ArrayList<Object> result = new ArrayList<>();
        Stack<BinaryNode> stack = new Stack<>();
        BinaryNode p = root;
        while(p != null || !stack.isEmpty()) {
            if (p != null) {
                stack.push(p);
                p = p.left;
            }else {
                BinaryNode pop = stack.pop();
                if (pop.key >= k1 && pop.key <= k2) {
                    result.add(pop.value);
                } else if (pop.key > k2) {
                    break;
                }
                p = pop.right;
            }
        }
            return result;
    }

        5.0 实现二叉搜索树核心方法的完整代码

实现接口代码:

import java.util.ArrayList;

import java.util.List;
import java.util.Stack;

public class BinaryTree implements BinarySearchTreeInterface{

    BinaryNode root = null;
    static class BinaryNode {
        int key;
        Object value;
        BinaryNode left;
        BinaryNode right;

        public BinaryNode(int kty, Object value) {
            this.key = kty;
            this.value = value;
        }

        public BinaryNode(int key, Object value, BinaryNode left, BinaryNode right) {
            this.key = key;
            this.value = value;
            this.left = left;
            this.right = right;
        }
    }

    @Override
    public Object get(int key) {
        if (root == null) {
            return null;
        }
        BinaryNode p = root;
        while(p != null) {
            if (p.key > key) {
                p = p.left;
            }else if (p.key < key) {
                p = p.right;
            }else {
                return p.value;
            }
        }
        return null;
    }

    @Override
    public Object min() {
        if (root == null) {
            return null;
        }
        BinaryNode p = root;
        while(p.left != null) {
            p = p.left;
        }
        return p.value;
    }
    public Object min(BinaryNode node) {
        if (node == null) {
            return null;
        }
        BinaryNode p = node;
        while (p.left != null) {
            p = p.left;
        }
        return p.value;
    }

    //使用递归实现找最小关键字
    public Object minRecursion() {
        return doMin(root);
    }
    private Object doMin(BinaryNode node) {
        if (node == null) {
            return null;
        }
        if (node.left == null) {
            return node.value;
        }
        return doMin(node.left);
    }


    @Override
    public Object max() {
        if (root == null) {
            return null;
        }
        BinaryNode p = root;
        while(p.right != null) {
            p = p.right;
        }
        return p.value;
    }
    public Object max(BinaryNode node) {
        if (node == null) {
            return null;
        }
        BinaryNode p = node;
        while (p.right != null) {
            p = p.right;
        }
        return p.value;
    }

    //使用递归实现找最大关键字
    public Object maxRecursion() {
        return doMax(root);
    }
    private Object doMax(BinaryNode node) {
        if (node == null) {
            return null;
        }
        if (node.right == null) {
            return node.value;
        }
        return doMax(node.right);
    }


    @Override
    public void put(int key, Object value) {
        if (root == null) {
            root = new BinaryNode(key,value);
            return;
        }
        BinaryNode p = root;
        BinaryNode parent = null;
        while (p != null) {
            parent = p;
            if (p.key > key) {
                p = p.left;
            } else if (p.key < key) {
                p = p.right;
            }else {
                p.value = value;
                return;
            }
        }

        //该树没有该关键字,因此需要新建节点对象
        BinaryNode newNode = new BinaryNode(key,value);
        if (newNode.key < parent.key) {
            parent.left = newNode;
        }else {
            parent.right = newNode;
        }

    }

    @Override
    public Object successor(int key) {
        if (root == null) {
            return null;
        }
        //先找到该关键字节点
        BinaryNode p = root;
        BinaryNode sParent = null;
        while (p != null) {
            if (p.key > key) {
                sParent = p;
                p = p.left;
            } else if (p.key < key) {
                p = p.right;
            }else {
                break;
            }
        }
        //没有找到关键字的情况
        if (p == null) {
            return null;
        }

        //情况一:该节点存在右子树,则该后继为右子树的最小关键字
        if (p.right != null) {
            return min(p.right);
        }

        //情况二:该节点不存在右子树,那么该后继就需要到祖宗从右向左的节点
        if (sParent == null) {
            //可能不存在后继节点,比如最大关键字的节点就没有后继节点了
            return null;
        }
        return sParent.value;
    }

    @Override
    public Object predecessor(int key) {
        if (root == null) {
            return null;
        }
        BinaryNode p = root;
        BinaryNode sParent = null;
        while (p != null) {
            if (p.key > key) {
                p = p.left;
            } else if (p.key < key) {
                sParent = p;
                p = p.right;
            }else {
                break;
            }
        }
        if (p == null) {
            return null;
        }
        //情况一:存在左子树,则该前任就为左子树的最大关键字节点
        if (p.left != null) {
            return max(p.left);
        }
        //情况二:不存在左子树,则该前任为从祖宗自左向右而来的节点
        if (sParent == null) {
            return null;
        }
        return sParent.value;
    }

    @Override
    public Object delete(int key) {
        if (root == null) {
            return null;
        }
        BinaryNode p = root;
        BinaryNode parent = null;
        while (p != null) {
            if (p.key > key) {
                parent = p;
                p = p.left;
            } else if (p.key < key) {
                parent = p;
                p = p.right;
            }else {
                break;
            }
        }
        //没有找到该关键字的节点
        if (p == null) {
            return null;
        }

        //情况一、二、三:只有左子树或者右子树或者都没有
        if (p.right == null) {
            shift(parent,p,p.left);
        } else if (p.left == null) {
            shift(parent,p,p.right);
        }else {
            //情况四:有左右子树
            //替换节点采用删除节点的后继节点
            //先看被删的节点与替换的节点是否为紧挨在一起
            BinaryNode s = p.right;
            BinaryNode sParent = p;
            while (s.left != null) {
                sParent = s;
                s = s.left;
            }
            if (sParent != p) {
                //说明没有紧挨在一起,则需要将替换节点的右子树进行处理
                shift(sParent,s,s.right);
                s.right = p.right;
            }
            shift(parent,p,s);
            s.left = p.left;
        }

        return p.value;
    }
    private void shift(BinaryNode parent, BinaryNode delete, BinaryNode next) {
        if (parent == null) {
            root = next;
        } else if (parent.left == delete) {
            parent.left = next;
        }else if (parent.right == delete){
            parent.right = next;
        }
    }

    //使用递归实现删除关键字节点
    public BinaryNode deleteRecursion(BinaryNode node , int key) {
        if (node == null) {
            return null;
        }
        if (node.key > key) {
            node.left = deleteRecursion(node.left,key);
            return node;
        } else if (node.key < key) {
            node.right = deleteRecursion(node.right,key);
            return node;
        }else {
            if (node.right == null) {
                return node.left;
            } else if (node.left == null) {
                return node.right;
            }else {
                BinaryNode s = node.right;
                while (s.left != null) {
                    s = s.left;
                }

                s.right = deleteRecursion(node.right,s.key);
                s.left = node.left;
                return s;
            }

        }
    }

    //找 < key 的所有 value
    public List<Object> less(int key) {
        if (root == null) {
            return null;
        }
        ArrayList<Object> result = new ArrayList<>();
        BinaryNode p = root;
        Stack<BinaryNode> stack = new Stack<>();
        while (p != null || !stack.isEmpty()) {
            if (p != null) {
                stack.push(p);
                p = p.left;
            }else {
                BinaryNode pop = stack.pop();
                if (pop.key < key) {
                    result.add(pop.value);
                }else {
                    break;
                }
                p = pop.right;
            }
        }
        return result;
    }

    //找 > key 的所有 value
    public List<Object> greater(int key) {
        if (root == null) {
            return null;
        }
        ArrayList<Object> result = new ArrayList<>();
        Stack<BinaryNode> stack = new Stack<>();
        BinaryNode p = root;
        while (p != null || !stack.isEmpty()) {
            if (p != null) {
                stack.push(p);
                p = p.left;
            }else {
                BinaryNode pop = stack.pop();
                if (pop.key > key) {
                    result.add(pop.value);
                }
                p = pop.right;
            }
        }
        return result;
    }
    //改进思路:遍历方向进行调整,先从右子树开始,再访问根节点,最后才到左子树
    public List<Object> greater1(int key) {
        if (root == null) {
            return null;
        }
        ArrayList<Object> result = new ArrayList<>();
        Stack<BinaryNode> stack = new Stack<>();
        BinaryNode p = root;
        while (p != null || !stack.isEmpty()) {
            if (p != null ) {
                stack.push(p);
                p = p.right;
            }else {
                BinaryNode pop = stack.pop();
                if (pop.key > key) {
                    result.add(pop.value);
                }else {
                    break;
                }
                p = pop.left;
            }
        }
        return result;
    }


    //找到 >= k1 且 =< k2 的所有value
    public List<Object> between(int k1, int k2) {
        if (root == null) {
            return null;
        }
        ArrayList<Object> result = new ArrayList<>();
        Stack<BinaryNode> stack = new Stack<>();
        BinaryNode p = root;
        while(p != null || !stack.isEmpty()) {
            if (p != null) {
                stack.push(p);
                p = p.left;
            }else {
                BinaryNode pop = stack.pop();
                if (pop.key >= k1 && pop.key <= k2) {
                    result.add(pop.value);
                } else if (pop.key > k2) {
                    break;
                }
                p = pop.right;
            }
        }
            return result;
    }

}

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/355973.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

MyBatis框架-ResultMap

文章目录 ResultMapsqlUser.java解决方案**方案一&#xff1a;为列名指定别名 , 别名和java实体类的属性名一致(自动映射)**UserMapper.xmlUserTest.java测试结果 **方案二&#xff1a;使用结果集映射->ResultMap 【推荐】&#xff08;手动映射&#xff09;**测试结果 Resul…

linux安装 黑方容灾备份与恢复系统软件v6.0 代理

1.环境准备 1.1硬件环境 内存>4G&#xff0c;cpu最低双核 1.2把SElinux状态改为Disabled &#xff08;1&#xff09;查看SElinux状态 输入getenforce命令 SELinux共有3个状态&#xff1a; enforcing &#xff08;执行中&#xff09;、permissive &#xff08;不执行但…

如何在Win系统安装Jupyter Notbook并实现无公网ip远程访问本地笔记

文章目录 1.前言2.Jupyter Notebook的安装2.1 Jupyter Notebook下载安装2.2 Jupyter Notebook的配置2.3 Cpolar下载安装 3.Cpolar端口设置3.1 Cpolar云端设置3.2.Cpolar本地设置 4.公网访问测试5.结语 1.前言 在数据分析工作中&#xff0c;使用最多的无疑就是各种函数、图表、…

YoloV8改进策略:Conv改进|DCNv4最新实践|高效涨点|多种改进教程|完整论文翻译

摘要 涨点效果:在我自己的数据集上,mAP50 由0.986涨到了0.993,mAP50-95由0.737涨到0.77,涨点明显! DCNv4是可变形卷积的第四版,速度和v3相比有了大幅度的提升,但是环境搭建有一定的难度,对新手不太友好。如果在使用过程遇到编译的问题,请严格按照我写的环境配置。 …

论文笔记:TimeGPT-1

时间序列的第一个基础大模型 1 方法 最basic的Transformer架构 采用了公开可用的最大时间序列数据集进行训练&#xff0c;包含超过1000亿个数据点。 训练集涵盖了来自金融、经济、人口统计、医疗保健、天气、物联网传感器数据、能源、网络流量、销售、交通和银行业等广泛领域…

自定义BeanNameGenerator生成规则

通过点进ComponentScan注解进入源码可以看到 追随BeanNameGenerator进入源码可以看到该类是个借口且只有一个方法 点击上面黑色箭头出现两个实现方法 点击第一个方法 进入determineBeanNameFromAnnotation方法中 通过上诉自定义一个生成beanName方法 先创建一个CustomeBeanN…

Docker本地部署APITable结合内网穿透实现公网访问

文章目录 前言1. 部署APITable2. cpolar的安装和注册3. 配置APITable公网访问地址4. 固定APITable公网地址 前言 vika维格表作为新一代数据生产力平台&#xff0c;是一款面向 API 的智能多维表格。它将复杂的可视化数据库、电子表格、实时在线协同、低代码开发技术四合为一&am…

QT+VS实现Kmeans++

1、Kmeans的原理如下&#xff1a; &#xff08;1&#xff09;首先选取样本中任一数据点作为第一个聚类中心&#xff1b; &#xff08;2&#xff09;计算样本每一个数据点至现所有聚类中心的最近距离&#xff0c;并记录下来&#xff1b; &#xff08;3&#xff09;逐一挑选所…

【C++】引用、内联函数、auto关键字等

前言&#xff1a;在前面我们讲解了C入门基础的一些学习例如命名空间、缺省参数、函数重载等。今天我们将进一步的学习&#xff0c;跟着博主的脚步再次往前迈一步吧。 &#x1f496; 博主CSDN主页:卫卫卫的个人主页 &#x1f49e; &#x1f449; 专栏分类:高质量&#xff23;学习…

SpringBoot 源码解析 - 持续更新

开始 spring initilizer&#xff1a;根据依赖构建工具、springboot 版本等生成 Java 工程。手把手教你手写一个最简单的 Spring Boot Starter Starter 命名规则 Spring 官方定义的 Starter 通常命名遵循的格式为 spring-boot-starter-{name}&#xff0c;例如 spring-boot-star…

目标检测数据集制作(VOC2007格式数据集制作和处理教程)

VOC2007数据集结构&#xff08;目标检测图像分割&#xff09; #VOC2007数据集结构如下&#xff1a; VOC2007|-Annotations#里面存放的是每一张图片对应的标注结果&#xff0c;为XML文件&#xff0c;#标注完成后JPEGImages每张图片在此都有一一对应的xml文件|-ImageSets#存放的是…

uniapp v3组合式语法-项目创建

打开uni编译器HbuildX,右键新建项目,出现如下界面 输入完项目名称和地址后.选择红色箭头所指处的vue版本3 然后点击确认,即可创建uniapp,v3项目 创建完项目后,可以看到如下项目结构 项目结构uniapp官网上已经写的很详细了,这里就使用uniapp官网的介绍 一个uni-app工程&…

用BufferedReader快速输入有巨坑!!大佬们都是这么输入的

刷洛谷题单第二天&#xff0c;今天第一道题还是挺简单的&#xff0c;但是又被现实给了一级大逼兜子&#xff0c;做这题关键还是想熟悉Java的快速输入。还有做这道题很容易出现RE错误&#xff0c;下面总结了常见的RE错误导致情况&#xff08;点击跳转下文&#xff09; 我的上篇…

增存量市场大爆发!国产通信中间件「反攻」

梳理2023年智能驾驶的发展脉络可见&#xff0c;消费者对智能驾驶的认可度和接受度越来越高&#xff0c;带动高速NOA迈向了规模化普及新阶段&#xff0c;城市NOA初露锋芒。 从更长远的行业变革周期来看&#xff0c;智能驾驶的技术迭代还在继续&#xff0c;叠加电子电气架构的深…

软件测试人员常用的功能测试方法分享

功能测试就是对产品的各功能进行验证&#xff0c;根据功能测试用例&#xff0c;逐项测试&#xff0c;检查产品是否达到用户要求的功能。 常用的测试方法如下&#xff1a; 1. 页面链接检查 每一个链接是否都有对应的页面&#xff0c;并且页面之间切换正确。 2. 相关性检查 删除/…

基于springboot的房屋交易系统

文章目录 项目介绍主要功能截图&#xff1a;部分代码展示设计总结项目获取方式 &#x1f345; 作者主页&#xff1a;超级无敌暴龙战士塔塔开 &#x1f345; 简介&#xff1a;Java领域优质创作者&#x1f3c6;、 简历模板、学习资料、面试题库【关注我&#xff0c;都给你】 &…

工业自动化步进电机用电机驱动芯片TMC5160

TMC5160 数据手册 TMC5160 集成了完整的运动控制功能、高质量的电流调节功能和强大的外部 MOSFET 驱动&#xff0c;涵盖了多种从电池供电的高效系统到 20 安培电机线圈电流的嵌入式应用。TMC5160 包含驱动电机所需的全部智能。配置 TMC5160 目 标 位 置 就 可 以 驱 动 电 机 。…

C++初阶:C/C++内存管理、new与delete详解

之前结束了类与对象&#xff1a;今天进行下面部分内容的学习 文章目录 1.C/C内存分布2.C语言中动态内存管理方式&#xff1a;malloc/calloc/realloc/free3.C动态内存管理方式3.1new/delete操作内置类型3.2new和delete操作自定义类型 4.operator new与operator delete函数5.new和…

题目:冒险者公会(蓝桥OJ 3611)

问题描述&#xff1a; 解题思路&#xff1a; 官方&#xff1a; 注意点&#xff1a; 在前期的排序操作&#xff0c;因为需要找到如样例所示的每轮最大&#xff0c;因此我们需要用0代替没有的委托&#xff08;即样例斜杠&#xff09;。如何用0代替&#xff1a;将村庄委托数量默认…

基于springboot校园交友网站源码和论文

随着信息技术和网络技术的飞速发展&#xff0c;人类已进入全新信息化时代&#xff0c;传统管理技术已无法高效&#xff0c;便捷地管理信息。为了迎合时代需求&#xff0c;优化管理效率&#xff0c;各种各样的管理系统应运而生&#xff0c;各行各业相继进入信息管理时代&#xf…