您的位置:首页 > 其它

算法_符号表

2016-07-07 12:12 309 查看
  符号表是一种存储键值对的数据结构,支持两种操作:插入(put),即将一组新的键值对存入表中;查找(get):即根据指定的键得到相应的值.

  实现的原则:

  每个键只对应一个值,表中不允许存在重复的键

  当用例代码向表中存入的键值对和表中已有的键(及关联的值)冲突时新的值会替代旧的值.

  而对于符号表来说,如果保持键的有序性,可以大大的扩展它的API,根据键的相对位置作出更多有用的操作.这种抽象的数据结构又被称为有序符号表.

  可以通过链表来实现无序符号表,代码如下:

  

public class SequentialSearchST<Key,Value> {
private Node first;     //链表首节点
private class Node {
Key key;
Value val;
Node next;
public Node(Key key,Value val,Node next) {
this.key=key;
this.val=val;
this.next=next;
}
}
public Value get(Key key) {
for(Node x=first;x!=null;x=x.next) {
if(key.equals(x.key)) {
return x.val;
}

}
return null;
}
public void put(Key key,Value val) {
for (Node x=first;x!=null;x=x.next) {
if(key.equals(x.key)) {
x.val=val;
return;
}
first=new Node(key,val,first);
}
}
}


  在这种实现中,未命中(不存在指定的键)的查找和插入操作都需要N次比较.命中的查找在最坏的情况下需要N次比较.特别的,向一个空表中,插入N个不同的键需要N2/2次比较.

  接下来通过一对平行的数组实现有序符号表.实现的核心是rank方法.它返回表中小于给定键的数量.由于使用二分查找可以大大减少每次查找的时候所需要的比较次数,因此采用二分查找来作为rank方法的基本实现.下面是代码:

public class BinarySearchST<Key extends Comparable<Key>,Value> {
private static final int INIT_CAPACITY = 2;
private Key[] keys;
private Value[] vals;
private int N = 0;

// create an empty symbol table with default initial capacity
public BinarySearchST() { this(INIT_CAPACITY); }

// create an empty symbol table with given initial capacity
public BinarySearchST(int capacity) {
keys = (Key[]) new Comparable[capacity];
vals = (Value[]) new Object[capacity];
}

private void resize(int capacity) /*动态的调整数组大小*/{
assert capacity >= N;
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] = vals[i];
}
vals = tempv;
keys = tempk;
}

// 是否包含指定的键
public boolean contains(Key key) {
return get(key) != null;
}

// 符号表所有的键的数量
public int size() {
return N;
}

// 符号表是否为空
public boolean isEmpty() {
return size() == 0;
}

//返回键对应的值,如果没有则返回null
public Value get(Key key) {
if (isEmpty()) return null;
int i = rank(key);
if (i < N && keys[i].compareTo(key) == 0) return vals[i];
return null;
}

// 采用二分查找法,获取key的位置
public int rank(Key key) {
int lo = 0, hi = N-1;
while (lo <= hi) {
int m = lo + (hi - lo) / 2;
int cmp = key.compareTo(keys[m]);
if      (cmp < 0) hi = m - 1;
else if (cmp > 0) lo = m + 1;
else return m;
}
return lo;
}

//寻找键,找到修改值,没有找到则新创建一个键值对.
public void put(Key key, Value val)  {
if (val == null) { delete(key); return; }

int i = rank(key);

// 键以及存在于符号表
if (i < N && keys[i].compareTo(key) == 0) {
vals[i] = val;
return;
}

// 插入键值对
if (N == keys.length) resize(2*keys.length);

for (int j = N; j > i; j--)  {
keys[j] = keys[j-1];
vals[j] = vals[j-1];
}
keys[i] = key;
vals[i] = val;
N++;

assert check();
}

// 移除键
public void delete(Key key)  {
if (isEmpty()) return;

// 获取位置
int i = rank(key);

// 不在符号表中
if (i == N || keys[i].compareTo(key) != 0) {
return;
}

for (int j = i; j < N-1; j++)  {
keys[j] = keys[j+1];
vals[j] = vals[j+1];
}

N--;
keys
= null;
vals
= null;

// resize if 1/4 full
if (N > 0 && N == keys.length/4) resize(keys.length/2);

assert check();
}

// 删除最小的键对应的值
public void deleteMin() {
if (isEmpty()) throw new RuntimeException("Symbol table underflow error");
delete(min());
}

// 删除最大的键对应的值
public void deleteMax() {
if (isEmpty()) throw new RuntimeException("Symbol table underflow error");
delete(max());
}

//最小的键
public Key min() {
if (isEmpty()) return null;
return keys[0];
}
//最大的键
public Key max() {
if (isEmpty()) return null;
return keys[N-1];
}
}


  对于N个键的有序数组中进行二分查找最多需要(lgN+1)次比较,向大小为N的有序数组中插入一个新的元素在最坏的情况下,需要访问~2N次数组,因此一个空符号表中插入N个元素在最坏情况下需要访问~N2次数组.
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: