数组实现的有序符号表介绍:
两个平行数组,相同的下标分别是键和值,由于查入和删除需要调整数组大小,所以和无序链表一样,这两个操作仍然是线性的。但是符号表最为频繁的操作应该是查询的,查询操作可以使用二分法实现,达到了logN的复杂度。二分法的实现需要排好序的键,所以有了泛型的存在,需要实现Comparable接口。rank函数用二分法查找键,在很多函数里面我们都会使用这个函数以达到快速查找的能力。由于有序符号表的实现可以基于多种数据结构,但是它们的操作都是符号表常见操作,故在这里抽象出一个SymbolTable类,包装好以后的符号表使用的函数声明原型。
Comparable:为了使任意类型可比较,JAVA包装好的Integer,Double..这些类都实现了这个接口。当前类的函数接受一个Comparable类型的参数,只要实现了这个接口的类都可以作为参数传递,这就是接口回调,类似于上转型对象。
有序符号表基类:
package com.lizi.datastructure.symboltable;
//有序符号表基类
public abstract class SymbolTable<Key extends Comparable<Key>,Value> {
//将键值对存入表中(若值为空则将建key从表中删除)
public abstract void put(Key key,Value value);
//获取键Key对应的值(若键key不存在返回空)
public abstract Value get(Key key);
//从表中删去键key(及其对应的值)
public abstract Value delete(Key key);
//表中是否存在该键
public boolean contains(Key key){
return get(key)!=null;
}
//表是否为空
public boolean isEmpty(){
return size()==0;
}
//表中的键值对数量
public abstract int size();
//最小的键
public abstract Key min();
//最大的键
public abstract Key max();
//小于等于Key的最大键
public abstract Key floor(Key key);
//大于等于Key的最小键
public abstract Key ceiling(Key key);
//小于key的键的数量
public abstract int rank(Key key);
//删除最小的键
public void deleteMin(){
delete(min());
}
//删除最大的键
public void deleteMax(){
delete(max());
}
//返回下标为index的键
public abstract Key select(int index);
//[low....high]之间键的数量,包括相等元素
public int size(Key low,Key high){
if (high.compareTo(low)<0)
return 0;
else if (contains(high))
return rank(high)-rank(low)+1;
else
return rank(high)-rank(low);
}
//[low....high]之间键的集合,已经排序
public abstract Iterable<Key> keys(Key low,Key high);
//表中所有键的集合,已经排序
public Iterable<Key> keys(){
return keys(min(),max());
}
}
以下是数组实现的符号表:
package com.lizi.datastructure.symboltable;
import java.util.ArrayList;
import java.util.List;
//二分搜索,基于排序数组
public class BinarySearchST<Key extends Comparable<Key>,Value> extends SymbolTable<Key, Value> {
private Key[] keys;
private Value[] values;
private int size=0;
@SuppressWarnings("unchecked")
public BinarySearchST(int capacity) {
keys=(Key[]) new Comparable[capacity];
values=(Value[]) new Object[capacity];//java不允许泛型数组,只能创建Object再强制转换类型
this.size=0;
}
@Override
public void put(Key key, Value value) {
if(value==null) {delete(key); return;}
//如果键存在,则修改键值
int pos=rank(key);
if (pos<size&&keys[pos].compareTo(key)==0) {
values[pos]=value;
return;
}
//键值不存在,判断数组是否越界并将数组扩容
if(size==keys.length) resize(2*keys.length);
for (int i =size; i>pos; i--) {
keys[i]=keys[i-1];
values[i]=values[i-1];
}
keys[pos]=key;
values[pos]=value;
size++;
}
@Override
public Value get(Key key) {
if(isEmpty()) return null;
int pos=rank(key);
if (pos<size&&keys[pos].compareTo(key)==0)
return values[pos];
else return null;
}
@Override
public Value delete(Key key) {
int pos=rank(key);
//没找到则返回空
if (pos<size&&keys[pos].compareTo(key)!=0) {
return null;
}
Value value = values[pos];
if(size<keys.length/2) resize(keys.length/2);
for (int i = pos; i < size - 1; i++) {
keys[i] = keys[i + 1];
values[i] = values[i + 1];
}
size--;
return value;
}
@Override
public int size() {
return size;
}
@Override
public Key min() {
return keys[0];
}
@Override
public Key max() {
return keys[size-1];
}
@Override
public Key floor(Key key) {
int pos=rank(key);
if (pos<size&&keys[pos].compareTo(key)==0) {
return keys[pos];
}
return keys[pos-1];
}
@Override
public Key ceiling(Key key) {
int pos=rank(key);
return keys[pos];
}
//非递归的二分查找
@Override
public int rank(Key key) {
int low=0;
int high=size-1;
while (low<=high) {
int middle=low+(high-low)/2;
int cmp=key.compareTo(keys[middle]);
if (cmp<0) high=middle-1;
else if(cmp>0) low=middle+1;
else return middle;
}
return low;
}
//递归的二分查找
public int rank(Key key,int low,int high) {
if(low>high) return low;
int pos=rank(key);
int cmp=key.compareTo(keys[pos]);
if (cmp>0) return rank(key, pos+1, high);
else if(cmp<0) return rank(key,low,pos-1);
else return pos;
}
@Override
public Key select(int index) {
return keys[index];
}
@Override
public Iterable<Key> keys(Key low, Key high) {
List<Key> keys=new ArrayList<Key>(size);
for (int i = 0; i <size; i++) {
keys.add(this.keys[i]);
}
return keys;
}
//该函数仅仅将容量扩大,但是有效元素数量并未改变,所以大小还是size
@SuppressWarnings("unchecked")
public void resize(int capacity) {
Key[] newKeys=(Key[]) new Comparable[capacity];
Value[] newValues=(Value[]) new Object[capacity];
for (int i = 0; i < size; i++) {
newKeys[i]=keys[i];
newValues[i]=values[i];
}
keys=newKeys;
values=newValues;
}
}