二分搜索树(二)

来源:互联网 发布:ps4没有备份数据 编辑:程序博客网 时间:2024/06/07 10:28

一.二分搜索树的顺序性
(1)求最小/大值 minimum/maximum
(2)求某个节点的前驱(某节点的左子树的最大值节点)/后继节点(某节点的右子树的最小值节点)predecessor,successor
p 是 d 的前驱, p = max(d->left)
p 是 d 的前驱, p = max(d->left)

s 是 d 的后继,s = min(d->right)
s 是 d 的后继,s = min(d->right)

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 ).value;    }    /**     * 判断根节点为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 Node search(Node root,Key key){        if (root == null){            return null;        }        if (key.compareTo(root.key) == 0){            return root;        }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);        }    }    /**     * 查找二分搜索树最小键值(非递归实现)     * @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 maximum(){        return maximum(root).key;    }    /**     * 返回以node为根的二分搜索树的最大键值所在的节点     * @param node     * @return     */    private Node maximum(Node node) {        if (node != null && node.right == null){            return node;        }else {            return maximum(node.right);        }    }    /**     * 查找二分搜索树最大键值(非递归实现)     * @param node     * @return     */    public Key maxmum(Node node){        Node maxNode = null;        while (node != null){            maxNode = node;            node = node.right;        }        return maxNode.key;    }    //从二分搜索树中删除最小值所在节点    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(maximum(node.left));            count++;            precursor.left = removeMax(node.left);            precursor.right = node.right;            node.left = node.right = null;            count--;            return precursor;*/        }    }    // 寻找key的floor值, 递归算法    // 如果不存在key的floor值(key比BST中的最小值还小), 返回NULL    public Key floor(Key key){        if( count == 0 || key.compareTo(minimum()) < 0 )            return null;        Node floorNode = floor(root, key);        return floorNode.key;    }    // 在以node为根的二叉搜索树中, 寻找key的floor值所处的节点, 递归算法    private Node floor(Node node, Key key){        if( node == null )            return null;        // 如果node的key值和要寻找的key值相等        // 则node本身就是key的floor节点        if( node.key.compareTo(key) == 0 )            return node;        // 如果node的key值比要寻找的key值大        // 则要寻找的key的floor节点一定在node的左子树中        if( node.key.compareTo(key) > 0 )            return floor( node.left , key );        // 如果node->key < key        // 则node有可能是key的floor节点, 也有可能不是(存在比node->key大但是小于key的其余节点)        // 需要尝试向node的右子树寻找一下        Node tempNode = floor( node.right , key );        if( tempNode != null )            return tempNode;        return node;    }    // 寻找key的ceil值, 递归算法    // 如果不存在key的ceil值(key比BST中的最大值还大), 返回NULL    Key ceil(Key key){        if( count == 0 || key.compareTo(maximum()) > 0 )            return null;        Node ceilNode = ceil(root, key);        return ceilNode.key;    }    // 在以node为根的二叉搜索树中, 寻找key的ceil值所处的节点, 递归算法    Node ceil(Node node, Key key){        if( node == null )            return null;        // 如果node的key值和要寻找的key值相等        // 则node本身就是key的ceil节点        if( node.key.compareTo(key) == 0 )            return node;        // 如果node的key值比要寻找的key值小        // 则要寻找的key的ceil节点一定在node的右子树中        if( node.key.compareTo(key) < 0 )            return ceil( node.right , key );        // 如果node->key > key        // 则node有可能是key的ceil节点, 也有可能不是(存在比node->key小但是大于key的其余节点)        // 需要尝试向node的左子树寻找一下        Node tempNode = ceil( node.left , key );        if( tempNode != null )            return tempNode;        return node;    }    // 查找key的前驱    // 如果不存在key的前驱(key不存在, 或者key是整棵二叉树中的最小值), 则返回NULL    public Key predecessor(Key key){        Node node = search(root, key);        // 如果key所在的节点不存在, 则key没有前驱, 返回NULL        if(node == null)            return null;        // 如果key所在的节点左子树不为空,则其左子树的最大值为key的前驱        if(node.left != null)            return maximum(node.left).key;        // 否则, key的前驱在从根节点到key的路径上, 在这个路径上寻找到比key小的最大值, 即为key的前驱        Node preNode = predecessorFromAncestor(root, key);        return preNode == null ? null : preNode.key;    }    // 在以node为根的二叉搜索树中, 寻找key的祖先中,比key小的最大值所在节点, 递归算法    // 算法调用前已保证key存在在以node为根的二叉树中    Node predecessorFromAncestor(Node node, Key key){        if(node.key.compareTo(key) == 0)            return null;        Node maxNode;        if(key.compareTo(node.key) < 0)            // 如果当前节点大于key, 则当前节点不可能是比key小的最大值            // 向下搜索到的结果直接返回            return predecessorFromAncestor(node.left, key);        else{            assert key.compareTo(node.key) > 0;            // 如果当前节点小于key, 则当前节点有可能是比key小的最大值            // 向下搜索结果存储到maxNode中            maxNode = predecessorFromAncestor(node.right, key);            if(maxNode != null)                // maxNode和当前节点node取最大值返回                return maxNode.key.compareTo(node.key) > 0 ? maxNode : node;            else                // 如果maxNode为空, 则当前节点即为结果                return node;        }    }    // 查找key的后继, 递归算法    // 如果不存在key的后继(key不存在, 或者key是整棵二叉树中的最大值), 则返回NULL    public Key successor(Key key){        Node node = search(root, key);        // 如果key所在的节点不存在, 则key没有前驱, 返回NULL        if(node == null)            return null;        // 如果key所在的节点右子树不为空,则其右子树的最小值为key的后继        if(node.right != null)            return minimum(node.right);        // 否则, key的后继在从根节点到key的路径上, 在这个路径上寻找到比key大的最小值, 即为key的后继        Node sucNode = successorFromAncestor(root, key);        return sucNode == null ? null : sucNode.key;    }    // 在以node为根的二叉搜索树中, 寻找key的祖先中,比key大的最小值所在节点, 递归算法    // 算法调用前已保证key存在在以node为根的二叉树中    Node successorFromAncestor(Node node, Key key){        if(node.key.compareTo(key) == 0)            return null;        Node minNode;        if(key.compareTo(node.key) > 0)            // 如果当前节点小于key, 则当前节点不可能是比key大的最小值            // 向下搜索到的结果直接返回            return successorFromAncestor(node.right, key);        else{            assert(key.compareTo(node.key) < 0);            // 如果当前节点大于key, 则当前节点有可能是比key大的最小值            // 向下搜索结果存储到minNode中            minNode = predecessorFromAncestor(node.left, key);            if(minNode != null)                // minNode和当前节点node取最小值返回                return minNode.key.compareTo(node.key) < 0 ? minNode : node;            else                // 如果minNode为空, 则当前节点即为结果                return node;        }    }    @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.maximum();        System.out.println(max);        Integer minimum = bst.minimum(bst.root);        System.out.println(minimum);        Integer maxmum = bst.maxmum(bst.root);        System.out.println(maxmum);        System.out.println("===============");        bst.levelOrder();        System.out.println();        bst.remove(30);        bst.levelOrder();        System.out.println();*/        System.out.println("-----------------------------------");        Integer floor = bst.floor(33);        System.out.println(floor);    }    @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.maximum() + " , ");            bst.removeMax();            System.out.println("After removeMax, size = " + bst.size() );        }    }}

(3)根据给定的值求它在整个二分搜索树中的floor和ceil,即小于给定值得最大节点和大于给定值的最小节点
这里写图片描述

  // 寻找key的floor值, 递归算法    // 如果不存在key的floor值(key比BST中的最小值还小), 返回NULL    public Key floor(Key key){        if( count == 0 || key.compareTo(minimum()) < 0 )            return null;        Node floorNode = floor(root, key);        return floorNode.key;    }    // 在以node为根的二叉搜索树中, 寻找key的floor值所处的节点, 递归算法    private Node floor(Node node, Key key){        if( node == null )            return null;        // 如果node的key值和要寻找的key值相等        // 则node本身就是key的floor节点        if( node.key.compareTo(key) == 0 )            return node;        // 如果node的key值比要寻找的key值大        // 则要寻找的key的floor节点一定在node的左子树中        if( node.key.compareTo(key) > 0 )            return floor( node.left , key );        // 如果node->key < key        // 则node有可能是key的floor节点, 也有可能不是(存在比node->key大但是小于key的其余节点)        // 需要尝试向node的右子树寻找一下        Node tempNode = floor( node.right , key );        if( tempNode != null )            return tempNode;        return node;    }    // 寻找key的ceil值, 递归算法    // 如果不存在key的ceil值(key比BST中的最大值还大), 返回NULL    Key ceil(Key key){        if( count == 0 || key.compareTo(maximun()) > 0 )            return null;        Node ceilNode = ceil(root, key);        return ceilNode.key;    }    // 在以node为根的二叉搜索树中, 寻找key的ceil值所处的节点, 递归算法    Node ceil(Node node, Key key){        if( node == null )            return null;        // 如果node的key值和要寻找的key值相等        // 则node本身就是key的ceil节点        if( node.key.compareTo(key) == 0 )            return node;        // 如果node的key值比要寻找的key值小        // 则要寻找的key的ceil节点一定在node的右子树中        if( node.key.compareTo(key) < 0 )            return ceil( node.right , key );        // 如果node->key > key        // 则node有可能是key的ceil节点, 也有可能不是(存在比node->key小但是大于key的其余节点)        // 需要尝试向node的左子树寻找一下        Node tempNode = ceil( node.left , key );        if( tempNode != null )            return tempNode;        return node;    }
package com.h.tree;import java.util.ArrayList;// 测试二分搜索树中的floor和ceil两个函数public class Main {    // 打乱数组顺序    private static void shuffle(ArrayList arr){        for(int i = arr.size()-1 ; i >= 0 ; i --){            int pos = (int) (Math.random() * (i+1));            Object t = arr.get(pos);            arr.set(pos, arr.get(i));            arr.set(i, t);        }    }    // 测试二分搜索树中的floor和ceil两个函数    public static void main(String[] args) {        BST<Integer, Integer> bst = new BST<Integer, Integer>();        //将[0, N)之间的偶数保存在nums中        int N = 1000;        ArrayList<Integer> nums = new ArrayList<Integer>();        for(int i = 0 ; i < N ; i += 2)            nums.add(i);        int minNum = nums.get(0);        int maxNum = nums.get(nums.size()-1);        // 将nums乱序处理        shuffle(nums);        // 向二分搜索树中插入[0, N)之间的所有偶数        for(Integer num: nums)            bst.insert(num, num);        // 对[0...N]区间里的N+1个数, 调用二分搜索树的floor和ceil, 查看其结果        for( int i = 0 ; i < N ; i ++ ){            // 测试floor            Integer floorKey = bst.floor(new Integer(i));            if(i % 2 == 0){                if(i >= 0 && i < N) assert floorKey == i;                else if(i < 0)      assert floorKey == null;                else                assert floorKey == maxNum;            }            else{                if(i - 1 >= 0 && i - 1 < N) assert floorKey == i - 1;                else if(i - 1 < 0)          assert floorKey == null;                else                        assert floorKey == maxNum;            }            System.out.print( "The floor of " + i + " is ");            if( floorKey == null )                System.out.println("NULL");            else                System.out.println(floorKey);            // 测试ceil            Integer ceilKey = bst.ceil(new Integer(i));            if(i % 2 == 0) {                if( i >= 0 && i < N ) assert ceilKey == i;                else if(i < 0)        assert ceilKey == minNum;                else                  assert ceilKey == null;            }            else{                if(i + 1 >= 0 && i + 1 < N) assert ceilKey == i + 1;                else if(i + 1 < 0)          assert ceilKey == minNum;                else                        assert ceilKey == null;            }            System.out.print( "the ceil of " + i + " is ");            if( ceilKey == null )                System.out.println("NULL");            else                System.out.println(ceilKey);            System.out.println();        }    }}
import java.util.ArrayList;// 测试二分搜索树中的floor和ceil两个函数public class Main {    // 打乱数组顺序    private static void shuffle(ArrayList arr){        for(int i = arr.size()-1 ; i >= 0 ; i --){            int pos = (int) (Math.random() * (i+1));            Object t = arr.get(pos);            arr.set(pos, arr.get(i));            arr.set(i, t);        }    }    // 测试二分搜索树中的predecessor和successor两个函数    public static void main(String[] args) {        // 生成 0 到 N-1 一共 N 个数字的数组        int N = 1000;        ArrayList<Integer> nums = new ArrayList<Integer>();        for( int i = 0 ; i < N ; i ++)            nums.add(i);        // 将数组中的数组乱序        shuffle(nums);        // 将这个N个数插入到二叉树中        BST<Integer, Integer> bst = new BST<Integer, Integer>();        for(Integer num: nums)            bst.insert(num, num);        // 测试前驱算法, 除了数字0没有前驱, 每个数字x的前驱应该为x-1        for(int i = 0 ; i < N ; i ++) {            if (i == 0) {                assert bst.predecessor(i) == null;                System.out.println("The predesessor of 0 is NULL");            } else {                assert bst.predecessor(i) == i - 1;                System.out.println("The predesessor of " + i + " is " + (i - 1));            }        }        System.out.println();        // 测试后继算法, 除了数字没有N-1后继, 每个数字x的后继应该为x+1        for(int i = 0 ; i < N ; i ++){            if( i == N-1 ){                assert bst.successor(i) == null;                System.out.println("The successor of " + i + " is NULL");            }            else{                assert bst.successor(i) == i+1;                System.out.println("The successor of " + i + " is " + (i+1));            }        }    }}

(4)rank和select
蓝色数字表示已当前节点为根节点的二分树的节点个数

这里写图片描述

(5)支持重复元素的二分搜索树
蓝色数字表示重复个数

问题:如何实现一棵支持重复元素,同时支持之前介绍所有操作的二分搜索树?

二.二分搜索树的局限性
同样的数据,可以对应不同的二分搜索树,可能退化为链表。
这里写图片描述

测试

package com.h.tree;import java.util.Collections;import java.util.Vector;/** * Created by John on 2017/10/3. */public class Main2 {    // 实验二分搜索树的局限性    public static void main(String[] args) {        // 我们使用文本量更小的共产主义宣言进行试验:)        String filename = "communist.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();            // 测试1: 我们按照文本原有顺序插入进二分搜索树            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);            }            // 我们查看unite一词的词频            if( bst.contain("unite") )                System.out.println("'unite' : " + bst.search("unite") );            else                System.out.println("No word 'unite' in " + filename);            long endTime = System.currentTimeMillis();            System.out.println("BST , time: " + (endTime - startTime) + "ms.");            System.out.println();            // 测试2: 我们按照文本原有顺序插入顺序查找表            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);            }            // 我们查看unite一词的词频            if( sst.contain("unite") )                System.out.println("'unite' : " + sst.search("unite") );            else                System.out.println("No word 'unite' in " + filename);            endTime = System.currentTimeMillis();            System.out.println("SST , time: " + (endTime - startTime) + "ms.");            System.out.println();            // 测试3: 我们将原文本排序后插入二分搜索树, 查看其效率            startTime = System.currentTimeMillis();            BST<String, Integer> bst2 = new BST<String, Integer>();            Collections.sort(words);            for (String word: words) {                Integer res = bst2.search(word);                if (res == null)                    bst2.insert(word, new Integer(1));                else                    bst2.insert(word, res + 1);            }            // 我们查看unite一词的词频            if( bst.contain("unite") )                System.out.println("'unite' : " + bst2.search("unite") );            else                System.out.println("No word 'unite' in " + filename);            endTime = System.currentTimeMillis();            System.out.println("BST2 , time: " + (endTime - startTime) + "ms.");        }    }}打印结果:There are totally 11349 words in communist.txt'unite' : 2BST , time: 28ms.'unite' : 2SST , time: 634ms.'unite' : 2BST2 , time: 947ms.

可以看出:同一组数据,因为可以有不同的二分搜索树结构,甚至在最坏的情况下,可以退化为链表,此时二分搜索树的深度变为了N(元素个数),之前的对二分搜索树的操作的算法复杂度变为了O(N)级别的。因此在二分搜索树的基础上引申出了平衡二叉树,典型的如红黑树,还有2-3tree,AVL tree,Splay tree,其他的还有平衡二叉树和堆的结合:Treap,trie。

非递归实现:

// 非递归的二分查找算法public class BinarySearch {    // 我们的算法类不允许产生任何实例    private BinarySearch() {}    // 二分查找法,在有序数组arr中,查找target    // 如果找到target,返回相应的索引index    // 如果没有找到target,返回-1    public static int find(Comparable[] arr, Comparable target) {        // 在arr[l...r]之中查找target        int l = 0, r = arr.length-1;        while( l <= r ){            //int mid = (l + r)/2;            // 防止极端情况下的整形溢出,使用下面的逻辑求出mid            int mid = l + (r-l)/2;            if( arr[mid].compareTo(target) == 0 )                return mid;            if( arr[mid].compareTo(target) > 0 )                r = mid - 1;            else                l = mid + 1;        }        return -1;    }    // 二分查找法, 在有序数组arr中, 查找target    // 如果找到target, 返回第一个target相应的索引index    // 如果没有找到target, 返回比target小的最大值相应的索引, 如果这个最大值有多个, 返回最大索引    // 如果这个target比整个数组的最小元素值还要小, 则不存在这个target的floor值, 返回-1    static int floor(Comparable[] arr, Comparable target){        // 寻找比target小的最大索引        int l = -1, r = arr.length-1;        while( l < r ){            // 使用向上取整避免死循环            int mid = l + (r-l+1)/2;            if( arr[mid].compareTo(target) >= 0 )                r = mid - 1;            else                l = mid;        }        assert l == r;        // 如果该索引+1就是target本身, 该索引+1即为返回值        if( l + 1 < arr.length && arr[l+1] == target )            return l + 1;        // 否则, 该索引即为返回值        return l;    }    // 二分查找法, 在有序数组arr中, 查找target    // 如果找到target, 返回最后一个target相应的索引index    // 如果没有找到target, 返回比target大的最小值相应的索引, 如果这个最小值有多个, 返回最小的索引    // 如果这个target比整个数组的最大元素值还要大, 则不存在这个target的ceil值, 返回整个数组元素个数n    static int ceil(Comparable[] arr, Comparable target){        // 寻找比target大的最小索引值        int l = 0, r = arr.length;        while( l < r ){            // 使用普通的向下取整即可避免死循环            int mid = l + (r-l)/2;            if( arr[mid].compareTo(target) <= 0 )                l = mid + 1;            else // arr[mid] > target                r = mid;        }        assert l == r;        // 如果该索引-1就是target本身, 该索引+1即为返回值        if( r - 1 >= 0 && arr[r-1] == target )            return r-1;        // 否则, 该索引即为返回值        return r;    }    // 测试我们用二分查找法实现的floor和ceil两个函数    // 请仔细观察在我们的测试用例中,有若干的重复元素,对于这些重复元素,floor和ceil计算结果的区别:)    public static void main(String[] args){        Integer arr[] = new Integer[]{1, 1, 1, 2, 2, 2, 2, 2, 4, 4, 5, 5, 5, 6, 6, 6};        for( int i = 0 ; i <= 8 ; i ++ ){            int floorIndex = floor(arr, i);            System.out.println("the floor index of " + i + " is " + floorIndex + ".");            if( floorIndex >= 0 && floorIndex < arr.length )                System.out.println("The value is " + arr[floorIndex] + ".");            System.out.println();            int ceilIndex = ceil(arr, i);            System.out.println("the ceil index of " + i + " is " + ceilIndex + ".");            if( ceilIndex >= 0 && ceilIndex < arr.length )                System.out.println("The value is " + arr[ceilIndex] + ".");            System.out.println();            System.out.println();        }    }}
原创粉丝点击