二分搜索树(二)
来源:互联网 发布:ps4没有备份数据 编辑:程序博客网 时间:2024/06/07 10:28
一.二分搜索树的顺序性
(1)求最小/大值 minimum/maximum
(2)求某个节点的前驱(某节点的左子树的最大值节点)/后继节点(某节点的右子树的最小值节点)predecessor,successor
p 是 d 的前驱, p = max(d->left)
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(); } }}
阅读全文
0 0
- 二分搜索树(二)
- 二分搜索应用二
- 最优二分搜索树
- 最优二分搜索树
- 二分搜索树
- 二分搜索树
- 二分搜索树--完整版
- 二分搜索树
- 二分搜索树(一)
- BST二分搜索树
- 二分搜索树
- 查找算法(二)二分搜索法
- NOJ1109搜索(二)——二分搜索
- 二分查找法与二分搜索树
- poj2104 线段树+二分搜索
- 二分搜索树完整实现
- 二分搜索树的实现
- java实现二分搜索树
- 数码管逐次加一同时流水灯
- Python yield 的理解
- leetcode 226. Invert Binary Tree
- 基于CENTOS6.5搭建Hadoop集群
- 数值计算方法(高斯消元以及LU分解)
- 二分搜索树(二)
- JAVA配置环境变量
- java并发中的ReentrantLock
- 进程间通信(2)-无名管道
- CS R26 C(双指针),D(观察,模拟),E(LIS经典 好题)
- 剑指offer——c++中,有哪4个与类型有关的关键字?它们各有的特点?应该在什么场合使用?
- MSP430切换主时钟
- jq代码学习14--表格变色
- 正则表达式