二分查詢實現符號表

inkparker發表於2020-11-10

二分查詢實現符號表

演算法原理

使用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;
    }
}

相關文章