查詢(二):有序符號表
阿新 • • 發佈:2019-02-15
1.2 有序符號表
一個無序的符號表幾乎是不可用的,因為其插入或查詢的時間複雜度總是O(N)。對於大量的資料的操作效能十分低下。因此,實現一個有序的符號表十分必要。
1.2.1 一個有序符號表API
方法名稱 | 功能 |
---|---|
void put(Key key,Value val) | 將鍵值存入表中(若值為空則將鍵key從表中刪除) |
Value get(Key key) | 獲取鍵key對於的值(若key不存在則返回null) |
void delete(Key key) | 從表中刪去鍵key(及其對於的值) |
boolean contains(Key key) | 鍵key是否存在於表中 |
boolean isEmpty() | 表是否為空 |
int size() | 表中鍵值對的數量 |
void rank(Key key) | 小於key的鍵的數量 |
void select(int i ) | 選擇排名為i的鍵 |
1.2.2 基於有序陣列的二分查詢符號表
有序陣列二分查詢依賴於一對平行陣列Key[],Value[]。對Key進行排序時,也要對Value進行相應的移動。
二分查詢符號表的核心方法是rank(Key key)。此方法返回的是比key小的元素的數量,在陣列中即key應該儲存的位置。
因此在插入操作時需要考慮到這種情況:
key比符號表中所有鍵都大,返回N,陣列不用移動。
public int rank(Key key){
'''N為現在符號表內鍵值對的數量'''
int lo = 0, hi = N - 1;
while(lo <= hi){
int mid = lo + (hi - lo) / 2;
'''通過compareTo方法比較大小,key大返回值大於0,key小返回值小於0,相等返回0'''
int cmp = key.compareTo(keys[mid]);
if(cmp < 0) hi = mid - 1 ;
else if(cmp > 0) lo = mid + 1;
else return mid;
}
return lo
}
下圖是基於有序陣列的符號表實現的軌跡圖
1.2.3 二分查詢符號表部分實現
public class BinarySearchST<Key extends Comparable<Key> , Value> {
private Key[] keys;
private Value[] vals;
private int N;
public BinarySearchST(int capacity){
this.keys = (Key[]) new Comparable[capacity];
this.vals = (Value[]) new Object[capacity];
this.N = 0;
}
public void put(Key key,Value val){
int i = rank(key);
//存在則覆蓋
if(i < N && keys[i].compareTo(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++;
}
public Value get(Key key){
if(isEmpty()) return null;
int i = rank(key);
if(i < N&&keys[i].compareTo(key) == 0) return vals[i];
else return null;
}
public boolean contains(Key key){
return this.get(key) != null;
}
public boolean isEmpty(){
return N == 0;
}
public int size(){
return N;
}
public Key min(){
return keys[0];
}
public Key max(){
return keys[N-1];
}
public int rank(Key key){
'''已在上方實現量'''
}
//rank方法的遞迴實現
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,mid+1,hi);
else if(cmp < 0) return rank(key,lo,mid-1);
else return mid;
}
public Key select(int k){
return keys[k];
}
}
1.2.4 二分查詢效能分析
二分查詢方法操作成本
方法名稱 | 時間複雜度 |
---|---|
void put(Key key,Value val) | N |
Value get(Key key) | logN |
void delete(Key key) | N |
boolean contains(Key key) | logN |
boolean isEmpty() | 1 |
int size() | 1 |
void rank(Key key) | lgN |
void select(int i ) | 1 |
簡單符號表實現的成本總結:
演算法 | 查詢(最壞) | 插入(最壞) |
---|---|---|
順序查詢(無序連結串列) | N | N |
二分查詢(有序陣列) | lgN | 2N |
分析結果
1.在N個鍵的有序陣列中進行二分查詢,最多需要(lgN+1)次比較(無論是否成功)
2.在大小為N的有序陣列中插入一個新的元素,在最壞情況下需要訪問~2N次陣列
3.在一個空表中插入N個元素在最壞情況下需要訪問~N2 次陣列
結論
基於有序陣列的二分查詢符號表在查詢方便確實有很大的提高,但是插入操作還是很慢,不適用於大規模的資料處理。