符号表
定义:符号表是一种存储键值对的数据结构,支持两种操作:插入(put),即将一组新的键值对存入表中;查好(get),即根据给定的键得到相应的值。
目的:符号表最主要的目的就是将一个键和一个值联系起来。用例能够将一个键值对插入符号表并希望在之后能够从符号表的所有键值对中按照键直接找到对应的值。
符号表是一种典型的抽象数据类型:它代表着一组定义清晰的值以及相应的操作,使得我们能够将类型的实现和使用区分开来。
顺序查找
在查找中我们一个一个地顺序遍历符号表中的所有键并使用equals()方法来寻找与被查找的键匹配的键。
算法:顺序查找(基于无序链表)
public class SequenialSearchST<Key,Value>{
pirvate Node first;
pirvate 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(){
for(Node x = firstl ; x != null ; x= x.next){
if(key.equals(x.key)){
x.val = val;
return;
}
}
first = new Node(key,val,first);
}
}
命题:在含有N对键值的基于(无序)链表的符号表中,未命中的查找和插入操作都需要N次比较。命中的查找在最坏情况下需要N次比较。特别地,向一个空表中插入N个不同的键需要~N^2/2次比较。
证明:在表中查找一个不存在的键时,我们会将表中的每个键和给定的键比较。因为不允许出现重复的键,每次插入操作之前我们都需要这样查找一遍。
推论:向一个空表中插入N个不同的键需要~N^2/2次比较。
查找一个已经存在的键并不需要线性级别的时间。一种度量方法是查找表中的每个键,并将总时间除以N。在查找中的每个键的可能性都相同的情况下时,这个结果就是依稀查找平均所有的比较数。我们将它称为随机命中。
有序数组中的二分查找
算法:二分查找(基于有序数组)
public class BinarySearchST<Key extends Comparable<Key>,Value>{
private Key[] keys;
private Value[] vals;
private int N;
public BinarySearchST(int capacity){
key = (Key[]) new Comparable[capacity];
vals = (Value[]) new Object[capacity];
}
public int size(){
return N;
}
public Value get(Key key){
if(isEmpty()) return null;
int i = rank(key);
if(i < N && keys[i].compare(key) == 0){
return vals[i];
}else{
return null;
}
}
publice int rank(Key key){
}
//查找键,找到则更新值,否则创建新的元素
public void put(Key key, Value val){
int i = rank(key);
if(i < N && keys[i].compare(key) == 0){
vals[i] = val;
return;
}
for(int j = N; j > i ; j--){
keys[j] = keys[j-1];
vals[j] = vals[j-1];
keys[i] = key;
vals[i] = val;
N++;
}
}
publice void delete(Key key){
}
}
在查找时,我们先将被查找的键和子数组的中间键比较。如果被查找的键小于中间键,我们就在左子数组中继续查找,如果大于我们就在右子数组中继续查找,否则中间键就是我们要找的键。
递归的二分查找
public int rank(Key key,int lo,int hi){
if(hi <lo){
return lo;
}
int mid = lo + (hi - lo)/2;
int cmp = key.compareTo(keys[mid]);
if(cmp < 0){
return rank(key,lo,mid-1);
}else if(cmp >0){
return rank(key,mid+1,hi)
}else{
return mid;
}
}
算法: 基于有序数组的二分查找(迭代)
public int rank(Key key){
int lo = 0;
int hi = N-1;
while(lo <= hi){
int mid = lo + (hi - lo)/2;
int cmp = key.compareTo(keys[mid]);
if(cmp < 0){
hi = mid -1;
}else if(cmp >0){
lo = mid +1;
}else {
return mid;
}
}
return lo;
}
使用的数据结构 | 实现 | 有点 | 缺点
————–|——|——|—-
链表()| SequentialSearchST| 适用于小型问题|对大型符号表很慢
二叉查找树
我们所使用的数据结构由结点组成,结点包含的链接可以指向空(null)或者其他结点。在二叉树中,每个结点只能有一个父结点指向自己(根结点没有父结点),而且每个结点都只有在左右两个链接,分别指向自己的左子结点,和右子结点。
定义:一颗二叉查找树是一颗二叉树,其中每个结点都包含有一个Comparable的键(以及相关联的值)且每个结点的键都大于其左子树中的任意结点的键而小于右子树的任意结点键。