二分搜索树(一)
来源:互联网 发布:提拉紧致面膜 知乎 编辑:程序博客网 时间: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.遍历操作
- 前序遍历:先访问当前节点,再依次递归访问左右子树
- 中序遍历:先递归访问左子树,再访问自身,再递归访问右子树
- 后续遍历:先递归访问左右子树,再访问自身节点
/** * 二叉树的深度优先遍历 */ //前序遍历 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() ); }}
阅读全文
0 0
- 二分搜索树(一)
- <每日一题>二分搜索
- 最优二分搜索树
- 最优二分搜索树
- 二分搜索树
- 二分搜索树
- 二分搜索树--完整版
- 二分搜索树
- 二分搜索树(二)
- BST二分搜索树
- 二分搜索树
- 每日一题7:二分搜索
- 二分查找法与二分搜索树
- poj2104 线段树+二分搜索
- 二分搜索树完整实现
- 二分搜索树的实现
- java实现二分搜索树
- 二分排序(搜索)树
- win32飞机大战
- SQLAlchemy的基本使用
- 单例模式
- Matplotlib 入门(三):多图合并
- C#如何根据字符串动态的创建对象(类)
- 二分搜索树(一)
- 最大比合并算法 (Maximal Ratio Combining, MRC)
- 解决线性回归算法的过拟合问题-----LARS算法
- 网站资源
- 浅谈如何在Mac上安装GDB调试
- POJ-1664 放苹果
- linux用户组以及权限总结
- 课堂练习2: 打印当前时间。学习使用Date类和Calendar类。
- Swing组件