二叉查找树支持将链表插入的灵活性和有序数组查找的高效性结合起来.数据结构由节点组成.节点包含的链接可以为null或者指向其他节点.在二叉树中,除了根节点以外,每个节点都有自己的父节点,且每个节点都只有左右两个链接,分别指向自己的左子节点和右子节点.因此可以将二叉查找树定义为一个空链接或者是一个有左右两个链接的节点,每个节点都指向一棵独立的子二叉树.
二叉查找树(BST)每个节点都包含有一个Comparable的键以及相关联的值,且每个节点的键都大于其左子树的任意节点的键而小于右子树的任意节点的键.
和链表一样,嵌套定义了一个私有类来表示二叉查找树上的节点,每个节点都含有一个键,一个值,一条左链接,一条右链接,和一个结点计数器(给出了以该节点为根的子树的节点总数).通过对于节点的递归操作实现了二叉树的基本插入,获取,删除操作,此外还支持和有序性相关的方法.代码如下:
1 import edu.princeton.cs.algs4.Queue; 2 3 public class BST<Key extends Comparable<Key>,Value> { 4 private Node root; //二叉查找树的根节点. 5 6 private class Node { 7 private Key key; //键 8 private Value val; //值 9 private Node left,right; //指向子树的链接 10 private int N; //以该结点为根的子树中的节点总数 11 public Node(Key key, Value val,int N) { 12 super(); 13 this.key = key; 14 this.val = val; 15 this.N=N; 16 } 17 } 18 19 public int size() { 20 return size(root); //满足size(x)=size(x.left)+size(x.right)+1 21 } 22 23 private int size(Node x) { 24 if(x==null) return 0; 25 else return x.N; 26 } 27 28 public Value get(Key key) { 29 return get(root,key); 30 } 31 32 private Value get(Node x, Key key) { 33 //以x为根节点的子树中查找并返回key所对应的值. 34 if(x==null) return null; 35 int cmp=key.compareTo(x.key); //与根节点的值作比较 36 if(cmp<0) return get(x.left,key); //如果小于根节点的值,那么在以x.left为根节点的子树寻找 37 if(cmp>0) return get(x.right,key); //如果大于根节点的值,那么在以x.right为根节点的子树寻找 38 else return x.val; //返回根节点的值. 39 } 40 41 public void put(Key key,Value val) { 42 //查找key,找到则更新它的值,否则为它创建一个新的节点. 43 root=put(root,key,val); 44 } 45 46 private Node put(Node x, Key key, Value val)/*x表示插入什么根节点*/ { 47 //如果key存在于以x为根节点的子树中则更新它的值.否则以key和val为键值对的新节点插入到该自述中 48 if(x==null) return new Node(key,val,1); 49 int cmp=key.compareTo(x.key); 50 if(cmp<0) x.left=put(x.left,key,val); //比根节点的键值小,则在左子树寻找,并更新左侧的根节点 51 else if(cmp>0) x.right=put(x.right,key,val);//比根节点的键值大,则在右子树寻找,并更新右侧的根节点 52 else x.val=val; //与根节点相同,更新值 53 x.N=size(x.left)+size(x.right)+1; //更新根节点的数目 54 return x; //更新根节点 55 } 56 public Key min() { //获取最小的键 57 return min(root).key; //从根节点开始向下获取 58 } 59 60 private Node min(Node x) { 61 if(x.left==null) return x; //如果没有左节点,则跟根节点的键值就是最小的键值 62 return min(x.left); //如果有左节点,则在左节点中寻找最小的键值 63 } 64 public Key max() { 65 return max(root).key; 66 } 67 public Node max(Node x) { 68 return max(x.right); 69 } 70 71 //小于等于key的最大键 72 public Key floor(Key key) { 73 Node x=floor(root,key); 74 if(x==null) return null; 75 return x.key; 76 } 77 78 private Node floor(Node x, Key key) { 79 if(x==null) return null; //key是最小的键 80 int cmp=key.compareTo(x.key); 81 if(cmp==0) return x; 82 if(cmp<0) return floor(x.left,key); //key位于根节点的左侧,因此小于等于key的键一定也位于左侧 83 Node t=floor(x.right,key); //key位于根节点的右侧. 84 if(t!=null) return t; //如果存在则返回存在的键值,不存在根节点就是小于等于key的最大键 85 else return x; 86 } 87 //返回排名为k的节点 88 public Key select(int k) { 89 return select(root,k).key; 90 } 91 private Node select(Node x, int k) { 92 if(x==null) return null; // 93 int t=size(x.left); //左子树的总个数 94 if(t>k) return select(x.left,k); //在左子树中寻找 95 if(t<k) return select(x.right,k-t-1); //在右子树中寻找 96 else return x; //返回x 97 } 98 public int rank(Key key) { 99 return rank(key,root); 100 } 101 //返回以x为根节点的子树中小于x.key的键的数量. 102 private int rank(Key key,Node x) { 103 if(x==null) return 0; 104 int cmp=key.compareTo(x.key); 105 if(cmp<0) return rank(key,x.left); 106 else if(cmp>0) return 1+size(x.left)+rank(key,x.right); 107 else return size(x.left); 108 } 109 //删除键值最小的子节点 110 public void deleteMin() { 111 root=deleteMin(root); 112 } 113 114 private Node deleteMin(Node x) { 115 if(x.left==null) return x.right; //将指向该结点的链接指向该结点的右子树. 116 x.left=deleteMin(x.left); //改变左节点的值. 117 x.N=size(x.left)+size(x.right)+1; 118 return x; 119 } 120 public void delete(Key key) { 121 root=delete(root,key); 122 } 123 124 public Node delete(Node x, Key key) { 125 if(x==null) return null; 126 int cmp=key.compareTo(x.key); 127 if(cmp<0) x.left=delete(x.left,key); 128 if(cmp>0) x.right=delete(x.right,key); 129 else { 130 if(x.right==null) return x.left; 131 if(x.left==null) return x.right; 132 Node t=x; //将要删除的节点赋值给t 133 x=min(t.right); //新节点为右子树中最小的节点 134 x.right=deleteMin(t.right); //将右子树更新 135 x.left=t.left; //左子树不变 136 } 137 x.N=size(x.left)+size(x.right)+1; 138 return x; 139 } 140 //查找操作. 141 public Iterable<Key> keys() { 142 return keys(min(),max()); 143 } 144 145 private Iterable<Key> keys(Key lo, Key hi) { 146 Queue<Key> queue=new Queue<Key>(); 147 keys(root,queue,lo,hi); 148 return queue; 149 } 150 151 private void keys(Node x, Queue<Key> queue, Key lo, Key hi) { 152 if(x==null) return ; 153 int cmplo=lo.compareTo(x.key); 154 int cmphi=hi.compareTo(x.key); 155 if(cmplo<0) keys(x.left,queue,lo,hi); //查找根节点的左子树 156 if(cmplo<=0&&cmphi>=0) queue.enqueue(x.key); //查找根节点 157 if(cmphi>0) keys(x.right,queue,lo,hi); //查找根节点的右子树 158 } 159 }
性能:对于二叉查找树而言:最坏情况下查找所需时间为N,插入所需时间为N,(时间与树的高度成正比),平均情况下,查找和插入所需时间为2InN,为了防止最坏情况的发生,即引出了平衡二叉查找树.对于二叉查找树,还有一种非递归的实现方法,效率更高,代码如下:
1 public class NonrecursiveBST<Key extends Comparable<Key>, Value> { 2 3 private Node root; 4 5 private class Node { 6 private Key key; 7 private Value value; 8 private Node left, right; 9 10 public Node(Key key, Value value) { 11 this.key = key; 12 this.value = value; 13 } 14 } 15 16 public void put(Key key, Value value) { 17 Node z = new Node(key, value); 18 if (root == null) { root = z; return; } 19 Node parent = null, x = root; 20 while (x != null) { 21 parent = x; 22 int res = key.compareTo(x.key); 23 if (res < 0) x = x.left; 24 else if (res > 0) x = x.right; 25 else { x.value = value; return; } 26 } 27 int res = key.compareTo(parent.key); 28 if (res < 0) parent.left = z; 29 else parent.right = z; 30 } 31 32 33 34 Value get(Key key) { 35 Node x = root; 36 while (x != null) { 37 int res = key.compareTo(x.key); 38 if (res < 0) x = x.left; 39 else if (res > 0) x = x.right; 40 else return x.value; 41 } 42 return null; 43 } 44 public Iterable<Key> keys() { 45 Queue<Key> queue = new Queue<Key>(); 46 keys(root, queue); 47 return queue; 48 } 49 private void keys(Node x, Queue<Key> queue) { 50 if (x == null) return; 51 keys(x.left, queue); 52 queue.enqueue(x.key); 53 keys(x.right, queue); 54 }