定义
定义:一个BST就是一棵二叉树,其中每个节点都含有一个Comparable的键(以及相关联的值value)且每个节点的键都大于其左子树的任意节点的键而小于右子树的任意节点的键。
数据结构
树由Node对象组成,每个对象有一对键值、两条链接和一个节点计数器N。每个Node对象都是一棵含有N个节点的子树的根节点,它的左链接指向一棵由小于该节点的所有键组成的二叉查找树,右链接指向一棵由大于该节点的所有键组成的二叉查找树。在BST类中,还应定义一个Node对象root,指向当前二叉树的根节点。
public class BST<Key extends Comparable<Key>, Value> { private class Node { private Key key; //键 private Value value; //值 private Node left,right; //左子树和右子树 private int N; //以该节点为根的子树中的所有结点总数 public Node(Key key, Value value, int N) { //注意需要将节点总数N作为参数 this.key = key; this.value = value; this.N = N; } } private Node root; //二叉查找树的根节点 private int size(Node x) { //作为私有方法,查找每棵子树的大小 if(x == null) return 0; else return x.N; } public int size() { //公有方法,求整棵树的节点总数 return size(root); } }
二叉查找树的操作
插入与查找操作
这两个操作较为简单,直接看代码:
public void put(Key key, Value value) { //查找key,找到更新其value,否则创建一个新节点,得到新的二叉查找树 root = put(root, key, value); } private Node put(Node x, Key key, Value value) { //如果key存在于以x为根的子树中则更新其value; //否则以key、value为新的节点插入到该子树的合适位置 if(x == null) return new Node(key, value, 1); int cmp = key.compareTo(x.key); if(cmp < 0) x.left = put(x.left, key, value); else if(cmp > 0) x.right = put(x.right, key, value); else x.value = value; x.N = size(x.left) + size(x.right) + 1; return x; } public Value get(Key key) { return get(root, key); } private Value get(Node x, Key key) { //在以x为根节点的树中查找并返回key所对应的值; //找不到则返回null if(x == null) return null; int cmp = key.compareTo(x.key); if(cmp < 0) return get(x.left, key); else if(cmp > 0) return get(x.right, key); else return x.value; }
在这里都是定义了一个公有方法和一个私有方法来实现一个操作,且都是用递归实现操作的,要注意节点数量要记得更改。
有序性相关方法和删除操作
最大键与最小键
如果根节点的左链接为空,那么一棵二叉查找树中最小的键就是根节点;如果左链接非空,则树中最小键就是左子树中的最小键。同理,最大键也是类似。
public Key min() { return min(root).key; } private Node min(Node x) { if(x.left == null) return x; return min(x.left); } public Key max() { return max(root).key; } private Node max(Node x) { if(x.right == null) return x; return max(x.right); }
选择与排名
public Key select(int k) { //返回排名为k的节点,排名从1开始 return select(root, k).key; } private Node select(Node x, int k) { if(x == null) return null;//排名从1开始 int t = size(x.left) + 1; if (t > k) return select(x.left, k); else if (t < k) return select(x.right, k - t); else return x; } public int rank(Key key) { //给定节点,返回其排名 return rank(root, key); } private int rank(Node x, Key key) { if(x == null) return 0; int cmp = key.compareTo(x.key); if (cmp < 0) return rank(x.left, key); else if (cmp > 0) return 1 + size(x.left) + rank(x.right, key); else return size(x.left) + 1;//排名从1开始 }
select操作时找到键排名为k的节点,rank操作则相反,是找到某个键的排名。
删除最大键与最小键
删除最小键:
- 往左递归遍历,直到节点的左链接为空;
- 用该节点的右节点替代该节点;
- 更新子树的节点数N。
public void deleteMin() { root = deleteMin(root); } private Node deleteMin(Node x) { //删除以结点x为根的子树的最小节点 if(x.left == null) return x.right; x.left = deleteMin(x.left); x.N = size(x.left) + size(x.right) + 1; return x; } public void deleteMax() { root = deleteMax(root); } private Node deleteMax(Node x) { //删除以结点x为根的子树的最大节点 if(x.right == null) return x.left; x.right = deleteMax(x.right); x.N = size(x.left) + size(x.right) + 1; return x; }
删除操作
我们可以用类似删除最小键的方式删除任意只有一个子节点的节点,但如何删除一个拥有两个子节点的节点?
步骤如下:
- 将指向即将被删除的节点的链接保存为t;
- 将x指向它的后继节点min(t.right);
- 将x的右链接指向deleteMin(t.right),也就是删除后所有结点仍然大于x.key的子二叉查找树;
- 将x的左链接(上图例子中为空)设为t.left。
在递归调用之后需要更新链接和节点计数器。
public void delete(Key key) { root = delete(root, key); } private Node delete(Node x, Key key) { //删除以x为根节点的树中key的这个节点 if(x == null) return null; int cmp = key.compareTo(x.key); if (cmp < 0) x.left = delete(x.left, key); else if (cmp > 0) x.right = delete(x.right, key); else { if(x.right == null) return x.left; if(x.left == null) return x.right; Node t = x; x = min(x.right); x.right = deleteMin(t.right); x.left = t.left; } x.N = size(x.left) + size(x.right) + 1; return x; }
java实现的完整代码
public class BST<Key extends Comparable<Key>, Value> { private class Node { private Key key; //键 private Value value; //值 private Node left,right; //左子树和右子树 private int N; //以该节点为根的子树中的所有结点总数 public Node(Key key, Value value, int N) { //注意需要将节点总数N作为参数 this.key = key; this.value = value; this.N = N; } } private Node root; //二叉查找树的根节点 private int size(Node x) { //作为私有方法,查找每棵子树的大小 if(x == null) return 0; else return x.N; } public int size() { //公有方法,求整棵树的节点总数 return size(root); } public void put(Key key, Value value) { //查找key,找到更新其value,否则创建一个新节点,得到新的二叉查找树 root = put(root, key, value); } private Node put(Node x, Key key, Value value) { //如果key存在于以x为根的子树中则更新其value; //否则以key、value为新的节点插入到该子树的合适位置 if(x == null) return new Node(key, value, 1); int cmp = key.compareTo(x.key); if(cmp < 0) x.left = put(x.left, key, value); else if(cmp > 0) x.right = put(x.right, key, value); else x.value = value; x.N = size(x.left) + size(x.right) + 1; return x; } public Value get(Key key) { return get(root, key); } private Value get(Node x, Key key) { //在以x为根节点的树中查找并返回key所对应的值; //找不到则返回null if(x == null) return null; int cmp = key.compareTo(x.key); if(cmp < 0) return get(x.left, key); else if(cmp > 0) return get(x.right, key); else return x.value; } public Key min() { return min(root).key; } private Node min(Node x) { if(x.left == null) return x; return min(x.left); } public Key max() { return max(root).key; } private Node max(Node x) { if(x.right == null) return x; return max(x.right); } public Key select(int k) { //返回排名为k的节点,排名从1开始 return select(root, k).key; } private Node select(Node x, int k) { if(x == null) return null;//排名从1开始 int t = size(x.left) + 1; if (t > k) return select(x.left, k); else if (t < k) return select(x.right, k - t); else return x; } public int rank(Key key) { //给定节点,返回其排名 return rank(root, key); } private int rank(Node x, Key key) { if(x == null) return 0; int cmp = key.compareTo(x.key); if (cmp < 0) return rank(x.left, key); else if (cmp > 0) return 1 + size(x.left) + rank(x.right, key); else return size(x.left) + 1;//排名从1开始 } public void deleteMin() { root = deleteMin(root); } private Node deleteMin(Node x) { //删除以结点x为根的子树的最小节点 if(x.left == null) return x.right; x.left = deleteMin(x.left); x.N = size(x.left) + size(x.right) + 1; return x; } public void deleteMax() { root = deleteMax(root); } private Node deleteMax(Node x) { //删除以结点x为根的子树的最大节点 if(x.right == null) return x.left; x.right = deleteMax(x.right); x.N = size(x.left) + size(x.right) + 1; return x; } public void delete(Key key) { root = delete(root, key); } private Node delete(Node x, Key key) { //删除以x为根节点的树中key的这个节点 if(x == null) return null; int cmp = key.compareTo(x.key); if (cmp < 0) x.left = delete(x.left, key); else if (cmp > 0) x.right = delete(x.right, key); else { if(x.right == null) return x.left; if(x.left == null) return x.right; Node t = x; x = min(x.right); x.right = deleteMin(t.right); x.left = t.left; } x.N = size(x.left) + size(x.right) + 1; return x; } public static void main(String[] args) { BST<String, Integer> bst = new BST<String, Integer>(); bst.put("a", 1); bst.put("f", 5); bst.put("b", 2); bst.put("c", 3); System.out.println(bst.max()); bst.delete("f"); System.out.println(bst.max()); } }