二分查找实现符号表
算法原理
使用Keys[]
和Values[]
两个数组分别存储键和值
实现的核心是rank()
方法,它返回表中小于给定键的数量
-
对于
get()
方法,只要给定键存在于表中,rank()
方法就能精确地告诉我们在哪里能够找到它(如果找不到,那么它肯定就不在表中) -
对于
put()
方法,只要给定键存在于表中,rank()
方法就能精确的告诉我们到哪里去更新它的值,当键不存在表中时也能精确的知道需要将键值对存储于何处。
性能分析
- 在 N N N 个键的有序数组中进行二分查找最多需要 l o g 2 N + 1 log_2N + 1 log2N+1次比较
- 向大小为 N N N的有序数组中插入一个新的元素在最坏情况下需要方位 2 N ~2N 2N次数组,因此像一个空符号表中插入 N N N个元素在最坏情况下需要访问 N 2 ~N^2 N2次数组
java 实现
/**
* 有序数组的二分查找
*/
public class ArrayBinarySearchST<Key extends Comparable<Key>, Value> implements IOrderedSymbolTable<Key, Value> {
private static final int INIT_CAPACITY = 2;
private Key[] keys;
private Value[] values;
private int n;
public ArrayBinarySearchST() {
this(INIT_CAPACITY);
}
public ArrayBinarySearchST(int capacity) {
keys = (Key[])new Comparable[capacity];
values = (Value[])new Object[capacity];
n = 0;
}
/**
* 返回最小的键
*
* @return 最小的键
*/
@Override
public Key min() {
if (isEmpty()) {
return null;
}
return keys[0];
}
/**
* 返回最大的键
*
* @return 最大的键
*/
@Override
public Key max() {
if (isEmpty()) {
return null;
}
return keys[n - 1];
}
/**
* 返回小于等于key的最大键
*
* @param key
* 键
* @return 小于等于key的最大键
*/
@Override
public Key floor(Key key) {
validKey(key);
int i = rank(key);
if (i < n && key.compareTo(keys[i]) == 0) {
return keys[i];
}
if (i == 0) {
return null;
}
return keys[i - 1];
}
/**
* 返回大于等于key的最小键
*
* @param key
* 键
* @return 大于等于key的最小键
*/
@Override
public Key ceiling(Key key) {
validKey(key);
int i = rank(key);
if (i == n) {
return null;
}
return keys[i];
}
/**
* 返回小于key的键的数量(键的排名)
*
* @param key
* 键
* @return 小于key的键的数量
*/
@Override
public int rank(Key key) {
validKey(key);
int low = 0;
int high = n - 1;
while (low <= high) {
int mid = low + (high - low) / 2;
int compare = key.compareTo(keys[mid]);
if (compare < 0) {
high = mid - 1;
} else if (compare > 0) {
low = mid + 1;
} else {
return mid;
}
}
return low;
}
/**
* 获取排名为k的键
*
* @param k
* 排名
* @return 排名为k的键
*/
@Override
public Key select(int k) {
if (k < 0 || k >= size()) {
throw new IllegalArgumentException("非法的select()参数: " + k);
}
return keys[k];
}
/**
* 删除最小的键值对
*/
@Override
public void deleteMin() {
delete(min());
}
/**
* 删除最大的键值对
*/
@Override
public void deleteMax() {
delete(max());
}
/**
* 返回[low..high]之间键的数量
*
* @param low
* 小键
* @param high
* 大键
* @return [low..high]之间键的数量
*/
@Override
public int size(Key low, Key high) {
validKey(low);
validKey(high);
if (low.compareTo(high) > 0) {
return 0;
}
if (contains(high)) {
return rank(high) - rank(low) + 1;
}
return rank(high) - rank(low);
}
/**
* 返回[low..high]之间的所有键,已排序
*
* @param lo
* 小键
* @param hi
* 大键
* @return [low..high]之间的所有键
*/
@Override
public Iterable<Key> keys(Key lo, Key hi) {
validKey(lo);
validKey(hi);
LinkedQueue<Key> queue = new LinkedQueue<>();
if (lo.compareTo(hi) > 0) {
return queue;
}
for (int i = rank(lo); i < rank(hi); i++) {
queue.enqueue(keys[i]);
}
if (contains(hi)) {
queue.enqueue(keys[rank(hi)]);
}
return queue;
}
/**
* 将键值对存入符号表(如果值为空则将键从表中删除)
*
* @param key
* 键
* @param value
* 值
*/
@Override
public void put(Key key, Value value) {
// 查找键,找到则更新值,否则创建新的元素
int i = rank(key);
if (value == null) {
delete(key);
return;
}
if (i < n && keys[i].compareTo(key) == 0) {
values[i] = value;
return;
}
if (n >= keys.length) {
resize(2 * keys.length);
}
// 将所有更大的键向后移动一格
for (int j = n; j > i; j--) {
keys[j] = keys[j - 1];
values[j] = values[j - 1];
}
keys[i] = key;
values[i] = value;
n++;
}
/***
* 获取键 key 的值(如果键 key 不存在则返回 null)
*
* @param key
* 键
* @return 对应的值
*/
@Override
public Value get(Key key) {
if (isEmpty()) {
return null;
}
int i = rank(key);
if (i < n && keys[i].compareTo(key) == 0) {
return values[i];
}
return null;
}
/**
* 从符号表中删除键 key(以及对应的值)
*
* @param key
* 键
*/
@Override
public void delete(Key key) {
validKey(key);
if (isEmpty()) {
return;
}
int i = rank(key);
if (i == n || key.compareTo(keys[i]) != 0) {
return;
}
for (int j = i; j < n - 1; j++) {
keys[j] = keys[j + 1];
values[j] = values[j + 1];
}
n--;
keys[n] = null;
values[n] = null;
if (n > 0 && n == keys.length / 4) {
resize(keys.length / 2);
}
}
/**
* 键key是否在符号表中有对应的值
*
* @param key
* 键
* @return boolean
*/
@Override
public boolean contains(Key key) {
return get(key) != null;
}
/**
* 返回符号表是否为空
*
* @return boolean
*/
@Override
public boolean isEmpty() {
return size() == 0;
}
/**
* 返回符号表中键值对的个数
*
* @return 符号表中键值对的个数
*/
@Override
public int size() {
return n;
}
/**
* 返回符号表中所有键的(可迭代)集合
*
* @return 键的集合
*/
@Override
public Iterable<Key> keys() {
return keys(min(), max());
}
private void validKey(Key key) {
if (key == null) {
throw new IllegalArgumentException();
}
}
private void resize(int capacity) {
Key[] tempk = (Key[])new Comparable[capacity];
Value[] tempv = (Value[])new Object[capacity];
for (int i = 0; i < n; i++) {
tempk[i] = keys[i];
tempv[i] = values[i];
}
values = tempv;
keys = tempk;
}
}