二叉搜索树简介说明

欣喜 Java教程 发布时间:2024-01-10 16:49:24 阅读数:16302 1
下文笔者讲述二叉搜索树的简介说明,如下所示

二叉搜索树的简介

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

二叉搜索树是一种树形结构

二叉搜索树的特点

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

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

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

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

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

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

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

 节点类的成员变量有:
    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;
        }
    }
}

实现二叉树的核心接口

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);
}

实现二叉搜索树 - 获取值 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;
    }

二叉搜索树 - 获取最小的关键字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);
    }
  

二叉搜索树 - 获取最大的关键字 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);
    }

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

在二叉搜索树中
    先试着查找是否存在与key对应的节点p.key
    当找到时
      则为更新该值 p.value = value 即可
    当找不到,则需要新增该关键字节点
    @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;
        }

    }

二叉搜索树 - 查找关键字的后驱节点 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;
    }

二叉搜索树 - 查找关键字的前驱节点 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;
    }

二叉搜索树 - 删除关键字节点 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;
        }
    }
    //使用递归实现删除关键字节点
    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;
            }
 
        }
    }

二叉搜索树 - 查找范围小于关键字的节点值 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;
    }

二叉搜索树 - 查找范围大于关键字的节点值 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;
    }

二叉搜索树 - 查找范围大于 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;
    }

二叉搜索树完整代码示例

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;
    }
 
} 
版权声明

本文仅代表作者观点,不代表本站立场。
本文系作者授权发表,未经许可,不得转载。

本文链接: https://www.Java265.com/JavaCourse/202401/7640.html

最近发表

热门文章

好文推荐

Java265.com

https://www.java265.com

站长统计|粤ICP备14097017号-3

Powered By Java265.com信息维护小组

使用手机扫描二维码

关注我们看更多资讯

java爱好者