一棵二叉查找树(BST)是一颗二叉树,其中每个节点都含有一个Comparable的键且每个节点的键(以及相关的值)都大于其左子树中的任意节点的键而小于右子树的任意结点的键。
数据表示
和链表一样,我们嵌套定义了一个私有类来表示二叉查找树上的一个节点。每个节点都含有一个键、一个值、一条左链接、一条右链接和一个节点计数器
查找节点
思路:
A、如果二叉查找树为空,查找失败(search miss),返回null;
B、如果根节点的键等于要查找的键,返回根节点的值(search hit)。
C、否则,继续在相应的子树中查找。如果要查找的键小于根节点的键,在左子树中查找;如果要查找的键大于根节点的键,在右子树中查找。
D、重复ABC步骤,直至search miss或者search hit。
插入节点
思路:
A、如果二叉查找树是空的,生成一个新节点,并返回该节点,相当于插入新节点后的二叉树根节点。
B、如果根节点键和要插入的键相等,更新根节点的值。
C、如果要插入的键小于根节点的键,在左子树插入,并将根节点的左链接指向插入后的左子树。
D、如果要插入的键小于根节点的键,在右子树插入,并将根节点的右链接指向插入后的右子树。
E、更新根节点的size,并返回根节点,作为插入新节点后的二叉树根节点。
F、重复ABCD,直至插入或者更新成功。
删除节点
删除节点是二叉搜索树中,最复杂的一种操作,但是也不是特别难,我们分类讨论:
- 要删除节点有零个孩子,即叶子节点
如图所示,只需要将parent.left设置为null,然后Java垃圾自动回收机制会自动删除current节点。
2.要删除节点有一个孩子
如图所示,只需要将parent.left设置为curren.right即可。
3.要删除节点有两个孩子
这种情况比较复杂,首先我们引入后继节点的概念,如果将一棵二叉树按照中序周游的方式输出,则任一节点的下一个节点就是该节点的后继节点。例如:上图中24的后继节点为26,64的后继节点为70.找到后继节点以后,问题就变得简单了,因为但删除节点有右子树,所以它的后继节点就是右子树中的最小节点。
分为两种情况:
- 后继节点为待删除节点的右子,只需要将curren用successor替换即可,注意处理好current.left和successor.right.
注意:这种情况下,successor一定没有左孩子,一但它有左孩子,哪它必然不是current的后继节点。
- 后继节点为待删除结点的右孩子的左子树,这种情况稍微复杂点,请看动态图片演示。
删除节点分4步
(1)将只想即将被删除的的节点链接保存为t
(2)将x指向它的后继节点min(t.right)
(3)将x的右链接指向deleteMin(x.right)
(4)将x的左链接设为t.left
实现
package find;public class BST, Value> { private Node root; public Value get(Key key) { return get(root, key); } //插入操作 public void put(Key key, Value val) { root = put(root, key, val); } //查询 private Value get(Node x, Key key) { 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.val; } } private Node put(Node x, Key key, Value val) { if (x == null) { return new Node(key, val, 1); } int cmp = key.compareTo(x.key); if (cmp < 0) { x.left = put(x.left, key, val); } else if (cmp > 0) { x.right = put(x.right, key, val); } else { x.val = val; } x.N = size(x.left) + size(x.right) + 1; return x; } public int size() { return size(root); } private int size(Node x) { if (x == null) { return 0; } else { return x.N; } } public Key min() { return min(root).key; } private Node min(Node x) { if (x.left == null) { return x; } return min(x.left); } public Key floor(Key key) { Node x = floor(root, key); if (x == null) { return null; } return x.key; } private Node floor(Node x, Key key) { if (x == null) return null; int cmp = key.compareTo(x.key); if (cmp == 0) { return x; } else if (cmp < 0) { return floor(x.left, key); } Node t = floor(x.right, key); if (t != null) { return t; } else { return x; } } public void deleteMin() { root = deleteMin(root); } private Node deleteMin(Node x) { if (x == null) { return x.right; } x.left = deleteMin(x.left); 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) { if (x == null) { return null; } int cmp = key.compareTo(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; } else if (x.left == null) { return x.right; } Node t = x; x = min(t.right); x.right = deleteMin(t.right); x.left = t.left; } x.N = size(x.left) + size(x.right) + 1; return x; } private class Node { private Key key; private Value val; private Node left, right; private int N; public Node(Key key, Value val, int N) { this.key = key; this.val = val; this.N = N; } }}