二分搜索树(一)

来源:互联网 发布:提拉紧致面膜 知乎 编辑:程序博客网 时间:2024/06/05 22:49

1.Binary Search Tree的优势
高效:不仅可查找数据;还可以高效地插入,删除数据 - 动态维护数据;可以方便地回答很多数据之间的关系问题min, max, floor, ceil, rank, select

这里写图片描述

这里写图片描述

二分搜索树不一定是完全二叉树(堆是一棵完全二叉树)
这里写图片描述

2.基本结构

package com.h.tree;/** * Created by John on 2017/9/24. * 二分搜索树 */// 由于Key需要能够进行比较,所以需要extends Comparable<Key>public class BST<Key extends Comparable<Key>, Value> {    // 树中的节点为私有的类, 外界不需要了解二分搜索树节点的具体实现    private class Node {        private Key key;        private Value value;        private Node left, right;        public Node(Key key, Value value) {            this.key = key;            this.value = value;            left = right = null;        }    }    private Node root;  // 根节点    private int count;  // 树中的节点个数    // 构造函数, 默认构造一棵空二分搜索树    public BST() {        root = null;        count = 0;    }    // 返回二分搜索树的节点个数    public int size() {        return count;    }    // 返回二分搜索树是否为空    public boolean isEmpty() {        return count == 0;    }}

基本操作:
1.插入新节点

/**     * 向二分搜索树中插入一个新的<Key,Value>数据对     * @param key     * @param value     */    public void insert(Key key,Value value){        root = insert(root,key,value);    }    private Node insert(Node root, Key key, Value value) {        //递归到底的情况        if (root == null){            count++;            return new Node(key,value);        }        //节点的Key相同,则value覆盖        if (key.compareTo(root.key) == 0){            root.value = value;        }else if (key.compareTo(root.key) < 0){            root.left = insert(root.left,key,value);        }else {            root.right = insert(root.right,key,value);        }        return root;    }

2.查找操作

 // 查看二分搜索树中是否存在键key    public boolean contain(Key key){        return contain(root, key);    }    // 在二分搜索树中搜索键key所对应的值。如果这个值不存在, 则返回null    public Value search(Key key){        return search( root , key );    }    /**     * 判断根节点为root的二分搜索树中是否包含key的节点     * @param root     * @param key     * @return     */    private boolean contain(Node root,Key key){        if (root == null){            return false;        }        if (key.compareTo(root.key) == 0){            return true;        }else if (key.compareTo(root.key) < 0){            return contain(root.left,key);        }else {            return contain(root.right,key);        }    }    /**     * 查找根节点为root的二分搜索树中Key=key的节点,若找到则返回对应的value,否则返回null     * @param root     * @param key     * @return     */    private Value search(Node root,Key key){        if (root == null){            return null;        }        if (key.compareTo(root.key) == 0){            return root.value;        }else if (key.compareTo(root.key) < 0){            return search(root.left,key);        }else {            return search(root.right,key);        }    }

测试用例:

 @org.junit.Test    public void test1(){        int N = 100;        // 创建一个数组,包含[0...N)的所有元素        Integer[] arr = new Integer[N];        for(int i = 0 ; i < N ; i ++){            arr[i] = new Integer(i);        }        // 打乱数组顺序        for(int i = 0 ; i < N ; i ++){            int pos = (int) (Math.random() * (i+1));            Integer t = arr[pos];            arr[pos] = arr[i];            arr[i] = t;        }       /* List<Integer> list = Arrays.asList(arr);        Collections.shuffle(list);        arr = (Integer[]) list.toArray();*/        /**         * 这里实现的二分搜索树不是平衡二叉树,         * 所以如果按照顺序插入一组数据,我们的二分搜索树会退化成为一个链表         * 平衡二叉树的实现,诸如红黑树的实现可以自行学习一下         */        // 这里测试用的的二分搜索树的键类型为Integer,值类型为String        // 键值的对应关系为每个整型对应代表这个整型的字符串        BST<Integer,String> bst = new BST<>();        for(int i = 0 ; i < N ; i ++){            bst.insert(new Integer(arr[i]), Integer.toString(arr[i]));        }        // 对[0...2*N)的所有整型测试在二分搜索树中查找        // 若i在[0...N)之间,则能查找到整型所对应的字符串        // 若i在[N...2*N)之间,则结果为null        for(int i = 0 ; i < 2*N ; i ++){            String res = bst.search(new Integer(i));            if( i < N ){                System.out.println(res.compareTo(Integer.toString(i) ) == 0? res:null);            }else {                System.out.println(res);            }        }    }

查找操作的实际应用案例:过滤关键词

package com.h.tree;/** * Created by John on 2017/9/24. * 顺序查找表 */public class SST<Key extends Comparable<Key>, Value> {    // 顺序查找表中的节点为私有的类, 外界不需要了解顺序查找法节点的具体实现    // 我们的顺序查找表, 内部本质是一个链表    private class Node {        private Key key;        private Value value;        private Node next;        public Node(Key key, Value value) {            this.key = key;            this.value = value;            next = null;        }    }    private Node head;  // 表头    private int count;  // 顺序查找表中的节点个数    // 构造函数    public SST(){        head = null;        count = 0;    }    // 返回顺序查找表中的节点个数    public int size(){        return count;    }    // 返回顺序查找表是否为空    public boolean isEmpty(){        return count == 0;    };    // 向顺序查找表中插入一个新的(key, value)数据对    public void insert(Key key, Value value){        // 查找一下整个顺序表,是否存在同样的key        Node node = head;        while( node != null ){            // 若在顺序表中找到了同样大小key的节点            // 则当前节点不需要插入,将该key所对应的值更新为value后返回            if( key.compareTo(node.key) == 0 ){                node.value = value;                return;            }            node = node.next;        }        // 若顺序表中没有同样的key,则创建新节点,将新节点直接插在表头        Node newNode = new Node(key, value);        newNode.next = head;        head = newNode;        count ++;    }    // 查看顺序查找表中是否包含键值为key的节点    public boolean contain(Key key){        Node node = head;        while( node != null ){            if( key.compareTo(node.key) == 0 )                return true;            node = node.next;        }        return false;    }    // 在顺序查找表中查找key所对应的value, 若value不存在, 则返回NULL    public Value search(Key key){        Node node = head;        while( node != null ){            if( key.compareTo(node.key) == 0 )                return node.value;            node = node.next;        }        return null;    }    // 在顺序查找表中删除(key,value)所对应的节点    public void remove(Key key){        if(head == null)            return;        // 如果待删除的节点就是头结点, 则需要特殊处理        // 思考: 对于链表, 可以使用什么技术不去特殊处理头结点的特殊情况?        // 更多和链表相关的算法问题, 欢迎大家看我的《玩儿转算法面试》课程 :)        if( key.compareTo(head.key) == 0 ){            Node delNode = head;            head = head.next;            delNode.next = null;            count--;            return;        }        Node node = head;        while( node.next != null && node.next.key.compareTo(key) != 0 )            node = node.next;        if( node.next != null ){            Node delNode = node.next;            node.next = delNode.next;            delNode.next = null;            count --;            return;        }    }}
package com.h.tree;import java.io.BufferedInputStream;import java.io.File;import java.io.FileInputStream;import java.io.IOException;import java.net.URL;import java.util.Locale;import java.util.Scanner;import java.util.Vector;// 文件相关操作public class FileOperations {    // 读取文件名称为filename中的内容,并将其中包含的所有词语放进words中    public static boolean readFile(String filename, Vector<String> words){        if (filename == null){            System.out.println("filename is null");            return false;        }        // 文件读取        Scanner scanner;        try {            /**             * getResource()方法会去classpath下找这个文件,获取到url resource,             * 得到这个资源后,调用url.getFile获取到 文件 的绝对路径             */            URL url = FileOperations.class.getClassLoader().getResource(filename);            /**             * url.getFile() 得到这个文件的绝对路径             */            File file = new File(url.getFile());            if( file.exists() ){                FileInputStream fis = new FileInputStream(file);                scanner = new Scanner(new BufferedInputStream(fis), "UTF-8");                scanner.useLocale(Locale.ENGLISH);            }else {                return false;            }        }        catch(IOException ioe){            System.out.println("Cannot open " + filename);            return false;        }        // 简单分词        // 这个分词方式相对简陋, 没有考虑很多文本处理中的特殊问题        // 在这里只做demo展示用        if (scanner.hasNextLine()) {            String contents = scanner.useDelimiter("\\A").next();            int start = firstCharacterIndex(contents, 0);            for (int i = start + 1; i <= contents.length(); i++){                if (i == contents.length() || !Character.isLetter(contents.charAt(i))) {                    String word = contents.substring(start, i).toLowerCase();                    words.add(word);                    start = firstCharacterIndex(contents, i);                    i = start + 1;                }            }        }        return true;    }    // 寻找字符串s中,从start的位置开始的第一个字母字符的位置    private static int firstCharacterIndex(String s, int start){        for( int i = start ; i < s.length() ; i ++ )            if( Character.isLetter(s.charAt(i)) )                return i;        return s.length();    }}
    @org.junit.Test    public void test(){        // 测试二分搜索树和顺序查找表之间的性能差距        // 二分搜索树的性能远远优于顺序查找表        // 使用圣经作为我们的测试用例        String filename = "bible.txt";        Vector<String> words = new Vector<>();        if(FileOperations.readFile(filename, words)){            System.out.println( "There are totally " + words.size() + " words in " + filename );            System.out.println();            // 测试 BST            long startTime = System.currentTimeMillis();            // 统计圣经中所有词的词频            // 注: 这个词频统计法相对简陋, 没有考虑很多文本处理中的特殊问题            // 在这里只做性能测试用            BST<String, Integer> bst = new BST<String, Integer>();            for (String word: words) {                Integer res = bst.search(word);                if (res == null)                    bst.insert(word, new Integer(1));                else                    bst.insert(word, res + 1);            }            // 输出圣经中god一词出现的频率            if( bst.contain("god") )                System.out.println("'god' : " + bst.search("god") );            else                System.out.println("No word 'god' in " + filename);            long endTime = System.currentTimeMillis();            System.out.println("BST , time: " + (endTime - startTime) + "ms.");            System.out.println();            // 测试顺序查找表 SST            startTime = System.currentTimeMillis();            // 统计圣经中所有词的词频            // 注: 这个词频统计法相对简陋, 没有考虑很多文本处理中的特殊问题            // 在这里只做性能测试用            SST<String, Integer> sst = new SST<String, Integer>();            for (String word: words) {                Integer res = sst.search(word);                if (res == null)                    sst.insert(word, new Integer(1));                else                    sst.insert(word, res + 1);            }            // 输出圣经中god一词出现的频率            if( sst.contain("god") )                System.out.println("'god' : " + sst.search("god") );            else                System.out.println("No word 'god' in " + filename);            endTime = System.currentTimeMillis();            System.out.println("SST , time: " + (endTime - startTime) + "ms.");        }    }

3.遍历操作

  1. 前序遍历:先访问当前节点,再依次递归访问左右子树
  2. 中序遍历:先递归访问左子树,再访问自身,再递归访问右子树
  3. 后续遍历:先递归访问左右子树,再访问自身节点
 /**  * 二叉树的深度优先遍历 */ //前序遍历    public void preOrder(){        preOrder(root);    }    private void preOrder(Node root) {        if (root != null){            System.out.println(root.key);            preOrder(root.left);            preOrder(root.right);        }    }    //中序遍历    public void inOrder(){        inOrder(root);    }    private void inOrder(Node root) {        if (root != null){            inOrder(root.left);            System.out.println(root.key);            inOrder(root.right);        }    }    //后序遍历    public void postOrder(){        postOrder(root);    }    private void postOrder(Node root) {        if (root != null){            postOrder(root.left);            postOrder(root.right);            System.out.println(root.key);        }    }
 @org.junit.Test    public void test2(){        // 测试二分搜索树的前中后序遍历        BST<Integer, Integer> bst = new BST<>();        // 取n个取值范围在[0...m)的随机整数放进二分搜索树中       /* int N = 10;        int M = 100;        for(int i = 0 ; i < N ; i ++){            Integer key = new Integer((int)(Math.random()*M));            // 为了后续测试方便,这里value值取和key值一样            bst.insert(key, key);            System.out.print(key + " ");        }*/        Integer[] arr = {28,16,30,13,22,29,42};        for (Integer i:arr){            bst.insert(i,i);        }        System.out.println();        // 测试二分搜索树的size()        System.out.println("size: " + bst.size());        System.out.println();        // 测试二分搜索树的前序遍历 preOrder        System.out.println("preOrder: ");        bst.preOrder();        System.out.println();        // 测试二分搜索树的中序遍历 inOrder        System.out.println("inOrder: ");        bst.inOrder();        System.out.println();        // 测试二分搜索树的后序遍历 postOrder        System.out.println("postOrder: ");        bst.postOrder();        System.out.println();        // 测试二分搜索树的层序遍历 levelOrder        System.out.println("levelOrder: ");        bst.levelOrder();        System.out.println();    }

从上面的结果可以看出来,中序遍历相当于给所有节点从小到大排了个序,而后序遍历有个很重要的应用,可以对二叉树进行节点的销毁。

 // 二分搜索树的层序遍历    public void levelOrder(){        // 我们使用LinkedList来作为我们的队列        LinkedList<Node> q = new LinkedList<>();        q.add(root);        while( !q.isEmpty() ){            Node node = q.remove();            System.out.println(node.key);            if( node.left != null ){                q.add( node.left );            }            if( node.right != null ){                q.add( node.right );            }        }    }

查找二分搜索树的最小/大值。

 /**     * 查找二分搜索树最小键值     * @return     */    public Key minimum(){         return minmum(root).key;    }    /**     * 返回以node为根的二分搜索树的最小键值所在的节点     * @param node     * @return     */    private Node minmum(Node node) {        if (node != null && node.left == null){             return node;        }else {            return minmum(node.left);        }    }     /**     * 查找二分搜索树最小键值(非递归实现)     * @param node     * @return     */    public Key minimum(Node node){        Node minNode = null;        while (node != null){            minNode = node;            node = node.left;        }        return minNode.key;    }       /**     * 查找二分搜索树最大键值     * @return     */    public Key maximun(){        return maximun(root).key;    }    /**     * 返回以node为根的二分搜索树的最大键值所在的节点     * @param node     * @return     */    private Node maximun(Node node) {        if (node != null && node.right == null){            return node;        }else {            return maximun(node.right);        }    }    /**     * 查找二分搜索树最大键值(非递归实现)     * @param node     * @return     */    public Key maxmum(Node node){        Node maxNode = null;        while (node != null){            maxNode = node;            node = node.right;        }        return maxNode.key;    }

4.删除节点
删除最小/大值节点

 //从二分搜索树中删除最小值所在节点    public void removeMin(){        root = removeMin(root);    }    /**     * 删除掉以node为根的二分搜索树中的最小节点     * 返回删除节点后新的二分搜索树的根     * @param node     * @return     */    private Node removeMin(Node node) {        if (node != null && node.left == null){            Node rightNode = node.right;            node.right = null;            count--;            return rightNode;        }        //将最小节点的右孩子取代最小节点        node.left = removeMin(node.left);        return node;    }    //从二分搜索树中删除最大值所在节点    public void removeMax(){        root = removeMax(root);    }    /**     * 删除掉以node为根的二分搜索树中的最大节点     * 返回删除节点后新的二分搜索树的根     * @param node     * @return     */    private Node removeMax(Node node) {        if (node != null && node.right == null){            Node leftNode = node.left;            node.right = null;            count--;            return leftNode;        }        node.right = removeMax(node.right);        return node;    }
  @org.junit.Test    public void test4(){        BST<Integer, Integer> bst = new BST<>();        //取n个取值范围在[0...m)的随机整数放进二分搜索树中        int N = 100;        int M = 100;        for(int i = 0 ; i < N ; i ++){            Integer key = new Integer((int)(Math.random()*M));            //为了后续测试方便,这里value值取和key值一样            bst.insert(key, key);        }        //注意, 由于随机生成的数据有重复, 所以bst中的数据数量大概率是小于n的        //测试 removeMin        //输出的元素应该是从小到大排列的        System.out.println("Test removeMin: ");        while( !bst.isEmpty() ){            System.out.print("min: " + bst.minimum() + " , ");            bst.removeMin();            System.out.println("After removeMin, size = " + bst.size() );        }        System.out.println();        for(int i = 0 ; i < N ; i ++){            Integer key = new Integer((int)(Math.random()*M));            //为了后续测试方便,这里value值取和key值一样            bst.insert(key, key);        }        //注意, 由于随机生成的数据有重复, 所以bst中的数据数量大概率是小于n的        //测试removeMax        //输出的元素应该是从大到小排列的        System.out.println("Test removeMax: ");        while( !bst.isEmpty() ){            System.out.print("max: " + bst.maximun() + " , ");            bst.removeMax();            System.out.println("After removeMax, size = " + bst.size() );        }    }

删除任意节点

package com.h.tree;import java.util.LinkedList;/** * Created by John on 2017/9/24. * 二分搜索树 */// 由于Key需要能够进行比较,所以需要extends Comparable<Key>public class BST<Key extends Comparable<Key>, Value> {    // 树中的节点为私有的类, 外界不需要了解二分搜索树节点的具体实现    private class Node {        private Key key;        private Value value;        private Node left, right;        public Node(Key key, Value value) {            this.key = key;            this.value = value;            left = right = null;        }        public Node(Node node){            this.key = node.key;            this.value = node.value;            this.left = node.left;            this.right = node.right;        }    }    private Node root;  // 根节点    private int count;  // 树中的节点个数    // 构造函数, 默认构造一棵空二分搜索树    public BST() {        root = null;        count = 0;    }    // 返回二分搜索树的节点个数    public int size() {        return count;    }    // 返回二分搜索树是否为空    public boolean isEmpty() {        return count == 0;    }    /**     * 向二分搜索树中插入一个新的<Key,Value>数据对     * @param key     * @param value     */    public void insert(Key key,Value value){        root = insert(root,key,value);    }    private Node insert(Node root, Key key, Value value) {        //递归到底的情况        if (root == null){            count++;            return new Node(key,value);        }        //节点的Key相同,则value覆盖        if (key.compareTo(root.key) == 0){            root.value = value;        }else if (key.compareTo(root.key) < 0){            root.left = insert(root.left,key,value);        }else {            root.right = insert(root.right,key,value);        }        return root;    }       // 查看二分搜索树中是否存在键key    public boolean contain(Key key){        return contain(root, key);    }    // 在二分搜索树中搜索键key所对应的值。如果这个值不存在, 则返回null    public Value search(Key key){        return search( root , key );    }    /**     * 判断根节点为root的二分搜索树中是否包含key的节点     * @param root     * @param key     * @return     */    private boolean contain(Node root,Key key){        if (root == null){            return false;        }        if (key.compareTo(root.key) == 0){            return true;        }else if (key.compareTo(root.key) < 0){            return contain(root.left,key);        }else {            return contain(root.right,key);        }    }    /**     * 查找根节点为root的二分搜索树中Key=key的节点,若找到则返回对应的value,否则返回null     * @param root     * @param key     * @return     */    private Value search(Node root,Key key){        if (root == null){            return null;        }        if (key.compareTo(root.key) == 0){            return root.value;        }else if (key.compareTo(root.key) < 0){            return search(root.left,key);        }else {            return search(root.right,key);        }    }    /**     * 二叉树的深度优先遍历     */    //前序遍历    public void preOrder(){        preOrder(root);    }    private void preOrder(Node root) {        if (root != null){            System.out.println(root.key);            preOrder(root.left);            preOrder(root.right);        }    }    //中序遍历    public void inOrder(){        inOrder(root);    }    private void inOrder(Node root) {        if (root != null){            inOrder(root.left);            System.out.println(root.key);            inOrder(root.right);        }    }    //后序遍历    public void postOrder(){        postOrder(root);    }    private void postOrder(Node root) {        if (root != null){            postOrder(root.left);            postOrder(root.right);            System.out.println(root.key);        }    }    // 二分搜索树的层序遍历    public void levelOrder(){        // 我们使用LinkedList来作为我们的队列        LinkedList<Node> q = new LinkedList<>();        q.add(root);        while( !q.isEmpty() ){            Node node = q.remove();            System.out.println(node.key);            if( node.left != null ){                q.add( node.left );            }            if( node.right != null ){                q.add( node.right );            }        }    }   /**     * 查找二分搜索树最小键值     * @return     */    public Key minimum(){         return minmum(root).key;    }    /**     * 返回以node为根的二分搜索树的最小键值所在的节点     * @param node     * @return     */    private Node minmum(Node node) {        if (node != null && node.left == null){             return node;        }else {            return minmum(node.left);        }    }    /**     * 查找二分搜索树最大键值     * @return     */    public Key maximun(){        return maximun(root).key;    }    /**     * 返回以node为根的二分搜索树的最大键值所在的节点     * @param node     * @return     */    private Node maximun(Node node) {        if (node != null && node.right == null){            return node;        }else {            return maximun(node.right);        }    }    //从二分搜索树中删除最小值所在节点    public void removeMin(){        root = removeMin(root);    }    /**     * 删除掉以node为根的二分搜索树中的最小节点     * 返回删除节点后新的二分搜索树的根     * @param node     * @return     */    private Node removeMin(Node node) {        if (node != null && node.left == null){            Node rightNode = node.right;            node.right = null;            count--;            return rightNode;        }        //将最小节点的右孩子取代最小节点        node.left = removeMin(node.left);        return node;    }    //从二分搜索树中删除最大值所在节点    public void removeMax(){        root = removeMax(root);    }    /**     * 删除掉以node为根的二分搜索树中的最大节点     * 返回删除节点后新的二分搜索树的根     * @param node     * @return     */    private Node removeMax(Node node) {        if (node != null && node.right == null){            Node leftNode = node.left;            node.right = null;            count--;            return leftNode;        }        node.right = removeMax(node.right);        return node;    }    // 从二分搜索树中删除键值为key的节点    public void remove(Key key){        root = remove(root, key);    }    /**     * 删除掉以node为根的二分搜索树中键值为key的节点, 递归算法     * 返回删除节点后新的二分搜索树的根     * @param node     * @param key     * @return     */    Node remove(Node node, Key key){        if( node == null )            return null;        if( key.compareTo(node.key) < 0 ){            node.left = remove( node.left , key );            return node;        }        else if( key.compareTo(node.key) > 0 ){            node.right = remove( node.right, key );            return node;        }        else{   // key == node.key            // 待删除节点左子树为空的情况            if( node.left == null ){                Node rightNode = node.right;                node.right = null;                count --;                return rightNode;            }            // 待删除节点右子树为空的情况            if( node.right == null ){                Node leftNode = node.left;                node.left = null;                count--;                return leftNode;            }            // 待删除节点左右子树均不为空的情况            // 找到比待删除节点大的最小节点, 即待删除节点右子树的最小节点            // 用这个节点顶替待删除节点的位置(后继顶替)            //这里不能直接使用Node successor = minmum(node.right);            Node successor = new Node(minmum(node.right));            count ++;            successor.right = removeMin(node.right);            successor.left = node.left;            node.left = node.right = null;            count --;            return successor;             //同样,也可以先找到比待删除节点小的最大节点,即待删除节点左子树的最大节点            //用这个节点代替待删除节点的位置(前驱顶替precursor)           /* Node precursor = new Node(maximun(node.left));            count++;            precursor.left = removeMax(node.left);            precursor.right = node.right;            node.left = node.right = null;            count--;            return precursor;*/        }    }    @org.junit.Test    public void test1(){        int N = 100;        // 创建一个数组,包含[0...N)的所有元素        Integer[] arr = new Integer[N];        for(int i = 0 ; i < N ; i ++){            arr[i] = new Integer(i);        }        // 打乱数组顺序        for(int i = 0 ; i < N ; i ++){            int pos = (int) (Math.random() * (i+1));            Integer t = arr[pos];            arr[pos] = arr[i];            arr[i] = t;        }       /* List<Integer> list = Arrays.asList(arr);        Collections.shuffle(list);        arr = (Integer[]) list.toArray();*/        /**         * 这里实现的二分搜索树不是平衡二叉树,         * 所以如果按照顺序插入一组数据,我们的二分搜索树会退化成为一个链表         * 平衡二叉树的实现,诸如红黑树的实现可以自行学习一下         */        // 这里测试用的的二分搜索树的键类型为Integer,值类型为String        // 键值的对应关系为每个整型对应代表这个整型的字符串        BST<Integer,String> bst = new BST<>();        for(int i = 0 ; i < N ; i ++){            bst.insert(new Integer(arr[i]), Integer.toString(arr[i]));        }        // 对[0...2*N)的所有整型测试在二分搜索树中查找        // 若i在[0...N)之间,则能查找到整型所对应的字符串        // 若i在[N...2*N)之间,则结果为null        for(int i = 0 ; i < 2*N ; i ++){            String res = bst.search(new Integer(i));            if( i < N ){                System.out.println(res.compareTo(Integer.toString(i) ) == 0? res:null);            }else {                System.out.println(res);            }        }    }    @org.junit.Test    public void test2(){        // 测试二分搜索树的前中后序遍历        BST<Integer, Integer> bst = new BST<>();        // 取n个取值范围在[0...m)的随机整数放进二分搜索树中       /* int N = 10;        int M = 100;        for(int i = 0 ; i < N ; i ++){            Integer key = new Integer((int)(Math.random()*M));            // 为了后续测试方便,这里value值取和key值一样            bst.insert(key, key);            System.out.print(key + " ");        }*/        Integer[] arr = {28,16,30,13,22,29,42};        for (Integer i:arr){            bst.insert(i,i);        }        System.out.println();        // 测试二分搜索树的size()        System.out.println("size: " + bst.size());        System.out.println();        // 测试二分搜索树的前序遍历 preOrder        System.out.println("preOrder: ");        bst.preOrder();        System.out.println();        // 测试二分搜索树的中序遍历 inOrder        System.out.println("inOrder: ");        bst.inOrder();        System.out.println();        // 测试二分搜索树的后序遍历 postOrder        System.out.println("postOrder: ");        bst.postOrder();        System.out.println();        // 测试二分搜索树的层序遍历 levelOrder        System.out.println("levelOrder: ");        bst.levelOrder();        System.out.println();    }    @org.junit.Test    public void test3(){        BST<Integer, Integer> bst = new BST<>();        Integer[] arr = {28,16,30,13,22,29,42};        for (Integer i:arr){            bst.insert(i,i);        }        System.out.println();        Integer min = bst.minimum();        System.out.println(min);        Integer max = bst.maximun();        System.out.println(max);    }    @org.junit.Test    public void test4(){        BST<Integer, Integer> bst = new BST<>();        // 取n个取值范围在[0...m)的随机整数放进二分搜索树中        int N = 100;        int M = 100;        for(int i = 0 ; i < N ; i ++){            Integer key = new Integer((int)(Math.random()*M));            // 为了后续测试方便,这里value值取和key值一样            bst.insert(key, key);        }        // 注意, 由于随机生成的数据有重复, 所以bst中的数据数量大概率是小于n的        // 测试 removeMin        // 输出的元素应该是从小到大排列的        System.out.println("Test removeMin: ");        while( !bst.isEmpty() ){            System.out.print("min: " + bst.minimum() + " , ");            bst.removeMin();            System.out.println("After removeMin, size = " + bst.size() );        }        System.out.println();        for(int i = 0 ; i < N ; i ++){            Integer key = new Integer((int)(Math.random()*M));            // 为了后续测试方便,这里value值取和key值一样            bst.insert(key, key);        }        // 注意, 由于随机生成的数据有重复, 所以bst中的数据数量大概率是小于n的        // 测试 removeMax        // 输出的元素应该是从大到小排列的        System.out.println("Test removeMax: ");        while( !bst.isEmpty() ){            System.out.print("max: " + bst.maximun() + " , ");            bst.removeMax();            System.out.println("After removeMax, size = " + bst.size() );        }    }}
package com.h.tree;public class Main {    // 打乱数组顺序    private static void shuffle(Object[] arr){        for(int i = arr.length-1 ; i >= 0 ; i --){            int pos = (int) (Math.random() * (i+1));            Object t = arr[pos];            arr[pos] = arr[i];            arr[i] = t;        }    }    // 测试二分搜索树中的remove    public static void main(String[] args) {        BST<Integer, Integer> bst = new BST<Integer, Integer>();        // 取n个取值范围在[0...n)的随机整数放进二分搜索树中        int N = 10000;        for(int i = 0 ; i < N ; i ++){            Integer key = new Integer((int)(Math.random()*N));            // 为了后续测试方便,这里value值取和key值一样            bst.insert(key, key);        }        // 注意, 由于随机生成的数据有重复, 所以bst中的数据数量大概率是小于n的        // order数组中存放[0...n)的所有元素        Integer order[] = new Integer[N];        for( int i = 0 ; i < N ; i ++ )            order[i] = new Integer(i);        // 打乱order数组的顺序        shuffle( order );        // 乱序删除[0...n)范围里的所有元素        for( int i = 0 ; i < N ; i ++ )            if( bst.contain( order[i] )){                bst.remove( order[i] );                System.out.println("After remove " + order[i] + " size = " + bst.size() );            }        // 最终整个二分搜索树应该为空        System.out.println( bst.size() );    }}
原创粉丝点击