二分查詢實現符號表
二分查詢實現符號表
演算法原理
使用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;
}
}
相關文章
- 查詢 - 符號表符號
- FastHook——實現.dynsym段和.symtab段符號查詢ASTHook符號
- PHP 實現二分查詢PHP
- Js實現二分查詢,加油JS
- Golang實現二分查詢法Golang
- 二分查詢(函式實現)函式
- 二分查詢實現----面試總結面試
- MyBatis Like查詢處理%_符號MyBatis符號
- Oracle特殊符號的模糊查詢Oracle符號
- Java實現二分查詢演算法Java演算法
- 查詢演算法集:順序查詢、二分查詢、插值查詢、動態查詢(陣列實現、連結串列實現)演算法陣列
- 查詢——二分查詢
- 二分法查詢(遞迴實現)遞迴
- java實現的二分法查詢Java
- 轉:C++實現的變種二分查詢法(折半查詢)--二叉查詢樹C++
- 二分查詢(一)——純粹的二分查詢
- Python查詢-二分查詢Python
- 二分查詢
- 用兩個二分查詢實現的海倫信封程式
- 查詢演算法__二分查詢演算法
- 順序查詢和二分查詢
- sqlserver查詢一張表所有欄位是否含製表符、換行符、回車符SQLServer
- CHARINDEX、表中有製表符、換行符、回車符的查詢和取消掉的方法Index
- PHP二分查詢PHP
- 二分查詢法
- 二分查詢的兩種實現形式遞迴和迭代遞迴
- 查詢演算法之二分查詢演算法
- 二分查詢 | 二分查詢的一種推薦寫法
- Spring Data JPA 實現聯表查詢Spring
- MYSQL INNODB中hash查詢表的實現MySql
- SQL多個表實現聯合查詢SQL
- python查詢演算法的實現-二分法Python演算法
- 【資料結構】折半查詢(二分查詢)資料結構
- linux下查詢無效的符號連線(轉)Linux符號
- 二分插入與二分查詢
- 二分查詢(c++)C++
- BinarySearch(二分查詢)
- 704.二分查詢