0 概述
本文将介绍能够将链表插入的灵活性和有序数组查找高性结合起来的符号表。具体来说,就是使用每个结点含有两个链接的二叉排序树(Binary Sort Tree)来高效实现符号表。
1 二叉排序树
二叉排序树或是一棵空树;或者具有下列性质的二叉树:
- 若它的左子树不空,则左子树上所有结点的值均小于它的根节点值
- 若它的右子树不空,则右子树上所有结点的值均大于它的根节点值
- 它的左右子树也分别为二叉排序树
- 按中序遍历该树所得到的中序序列是一个递增有序序列。
1.1 查找
二叉排序树查找比较简单。如果二叉排序树不为空,首先和根节点比较,相等表示查找成功,否则将根据给定值和根节点的关键字之间的大小关系,分别在左子树或右子树上继续查找。
1.2 插入
二叉排序树插入相对也比较简单,就是在查找的基础,如果根据键查到了相应的节点则更新节点就好,如果没有查找到,在遍历的最后节点的左孩子或者右孩子上插入一个新的节点。
1.3 删除
二叉排序树排序树的删除相对复杂一些,删除分为两种情况
1.删除节点至多只有一个孩子
2.删除的节点同时存在左右孩子
对于第一种情况:直接删除该节点,将其左孩子或者右孩子替换原来的节点。要删除150这个节点,找到这个节点后,其只有一个孩子
直接将该孩子接替这个节点(如下图所示)。
对于第二种情况:删除的节点同时存在左右孩子,此时不能直接删除,需要找到该节点的后继节点,然后和后继节点交换,然后对应删除后继节点。
后继节点的求法:
2 代码实现
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Stack;
/**
* Created by hsc on 17/6/19.
*/
public class BinarySortTreeST<Key, Value> implements SymbolTable<Key, Value> {
private Node root;
private int size;
private class Node {
private Key key; // the key
private Value val; // the associated value
private Node left; // left subtree
private Node right; // right subtree
public Node(Key key, Value val) {
this.key = key;
this.val = val;
}
}
private class WrapperNode {
private Node currentNode;
private Node parentNode;
public WrapperNode(Node currentNode, Node parentNode) {
this.currentNode = currentNode;
this.parentNode = parentNode;
}
}
@Override
public Value put(Key key, Value value) {
checkKey(key);
if (root == null) {
root = new Node(key, value);
size++;
return root.val;
}
WrapperNode wrapperNode = binarySearch(key);
if (wrapperNode.currentNode != null) {
wrapperNode.currentNode.val = value;
return wrapperNode.currentNode.val;
}
Node parentNode = wrapperNode.parentNode;
int cmp = compare(key, parentNode.key);
if (cmp > 0) {
size++;
parentNode.right = new Node(key, value);
} else if (cmp < 0) {
size++;
parentNode.left = new Node(key, value);
}
return value;
}
@Override
public Value get(Key key) {
checkKey(key);
Node node = binarySearch(key).currentNode;
return node == null ? null : node.val;
}
@Override
public Value delete(Key key) {
if (isEmpty()) {
return null;
}
WrapperNode wrapperNode = binarySearch(key);//搜索到当前节点
Node delNode = wrapperNode.currentNode;
if (delNode == null || compare(key, delNode.key) != 0) {
return null;
}
if (delNode.left == null || delNode.right == null) {
return deleteNode(delNode, wrapperNode.parentNode);
} else {
WrapperNode tmpWrapperNode = successor(delNode);
if (tmpWrapperNode.currentNode != null) {
Value tempVal = delNode.val;
Key tempKey = delNode.key;
delNode.val = tmpWrapperNode.currentNode.val;
delNode.key = tmpWrapperNode.currentNode.key;
tmpWrapperNode.currentNode.val = tempVal;
tmpWrapperNode.currentNode.key = tempKey;
return deleteNode(tmpWrapperNode.currentNode, tmpWrapperNode.parentNode);
}
}
return null;
}
@Override
public boolean containsKey(Key key) {
checkKey(key);
return binarySearch(key).currentNode != null;
}
@Override
public boolean isEmpty() {
return this.size() == 0;
}
@Override
public int size() {
return size;
}
@Override
public Iterator<Key> keys() {
List<Key> list = new ArrayList<>(size);
Stack<Node> tempStack = new Stack<>();
Node pNode = root;
while (pNode != null || !tempStack.empty()) {
while (pNode != null) {
tempStack.push(pNode);
pNode = pNode.left;
}
if (!tempStack.isEmpty()) {
pNode = tempStack.pop();
list.add(pNode.key);
pNode = pNode.right;
}
}
return list.iterator();
}
@SuppressWarnings("unchecked")
private int compare(Object k1, Object k2) {
return ((Comparable<? super Key>) k1).compareTo((Key) k2);
}
private void checkKey(Key key) {
if (key == null) {
throw new IllegalArgumentException("argument is null");
}
}
/**
* 获取当前节点的后继节点
*/
private WrapperNode successor(Node node) {
Node currentNode = node.right;
Node parentNode = node;
while (currentNode != null) {
if (currentNode.left == null) {
break;
}
parentNode = currentNode;
currentNode = currentNode.left;
}
return new WrapperNode(currentNode, parentNode);
}
/**
* 从根节点查找,返回查找到节点以及其双亲节点
*/
private WrapperNode binarySearch(Key key) {
Node currentNode = root;
Node parentNode = root;
while (currentNode != null) {
int cmp = compare(key, currentNode.key);
parentNode = cmp != 0 ? currentNode : parentNode;
if (cmp > 0) {
currentNode = currentNode.right;
} else if (cmp < 0) {
currentNode = currentNode.left;
} else {
break;
}
}
return new WrapperNode(currentNode, parentNode);
}
/**
* 删除节点,将其做孩子或者右孩子直接替换成该节点
*/
private Value deleteNode(Node delNode, Node parentNode) {
Node linkNode = delNode.right == null ? delNode.left : delNode.right;
//如果要删除节点是根节点
if (delNode == root) {
root = linkNode;
} else {
if (parentNode.left == delNode) {
parentNode.left = linkNode;
} else if (parentNode.right == delNode) {
parentNode.right = linkNode;
}
}
size--;
return delNode.val;
}
}
3 总结
二叉排序在极端的情况下,会退化成链表,查询时间复杂度就和链表一样,后续会引入平衡二叉树。