HashTable 实现

根据冲突解决的不同,可分为seperate chaining hash table, linear probing hash table, quadratic probing hash table.

 

自己实现的最简单饿 seperate chaining hash table。

package ADT;

import java.util.LinkedList;
/**
 * 自己实现的屌丝版的MySeperateChainingHashTable,
 * 
 * @author jd
 *
 * @param <K>
 * @param <V>
 */
public class MySeperateChainingHashTable<K, V> {
    private static int M = 10;
    private LinkedList<Entry<K, V>>[] items;

    public MySeperateChainingHashTable() {
        items = (LinkedList<Entry<K, V>>[]) (new LinkedList[M]);
        for (int i = 0; i < M; i++) {
            items[i] = new LinkedList<Entry<K, V>>();
        }
    }

    public void put(K key, V value) {
        int idx = hash(key);
        LinkedList<Entry<K, V>> list = items[idx];
        for (Entry<K, V> each : list) {
            if (each.key.equals(key)) {
                each.value = value;
                return;
            }
        }

        list.add(new Entry<K,V>(key, value));
    }

    public V get(K key) {
        int idx = hash(key);
        LinkedList<Entry<K, V>> list = items[idx];
        for (Entry<K, V> each : list) {
            if (each.key.equals(key)) {
                return each.value;
            }
        }

        return null;
    }

    private int hash(K key) {
        int res = key.hashCode();
        if (res < 0)
            res += M;
        return res % M;
    }

    private static class Entry<K, V> {
        public K key;
        public V value;

        public Entry(K key, V value) {
            this.key = key;
            this.value = value;
        }

    }

    public static void main(String[] args) {
        MySeperateChainingHashTable<Integer, String> hashtable = new MySeperateChainingHashTable<Integer, String>();

        for (int i = 0; i < 100; i++) {
            hashtable.put(i, i + "" + i);
        }

        for (int i = 0; i < 100; i++) {
            System.out.println(hashtable.get(i));
        }

    }

}
View Code

 

 

教材里的范例代码:seperate chaining hash table

package ADT;

import java.util.LinkedList;
import java.util.List;

// SeparateChaining Hash table class
//
// CONSTRUCTION: an approximate initial size or default of 101
//
// ******************PUBLIC OPERATIONS*********************
// void insert( x )       --> Insert x
// void remove( x )       --> Remove x
// boolean contains( x )  --> Return true if x is present
// void makeEmpty( )      --> Remove all items

/**
 * Separate chaining table implementation of hash tables. Note that all
 * "matching" is based on the equals method.
 * 
 * @author Mark Allen Weiss
 */
public class SeparateChainingHashTable<T> {
    /**
     * Construct the hash table.
     */
    public SeparateChainingHashTable() {
        this(DEFAULT_TABLE_SIZE);
    }

    /**
     * Construct the hash table.
     * 
     * @param size
     *            approximate table size.
     */
    public SeparateChainingHashTable(int size) {
        theLists = new LinkedList[nextPrime(size)];
        for (int i = 0; i < theLists.length; i++)
            theLists[i] = new LinkedList<>();
    }

    /**
     * Insert into the hash table. If the item is already present, then do
     * nothing.
     * 
     * @param x
     *            the item to insert.
     */
    public void insert(T x) {
        List<T> whichList = theLists[myhash(x)];
        if (!whichList.contains(x)) {
            whichList.add(x);

            // Rehash; see Section 5.5
            if (++currentSize > theLists.length)
                rehash();
        }
    }

    /**
     * Remove from the hash table.
     * 
     * @param x
     *            the item to remove.
     */
    public void remove(T x) {
        List<T> whichList = theLists[myhash(x)];
        if (whichList.contains(x)) {
            whichList.remove(x);
            currentSize--;
        }
    }

    /**
     * Find an item in the hash table.
     * 
     * @param x
     *            the item to search for.
     * @return true if x isnot found.
     */
    public boolean contains(T x) {
        List<T> whichList = theLists[myhash(x)];
        return whichList.contains(x);
    }

    /**
     * Make the hash table logically empty.
     */
    public void makeEmpty() {
        for (int i = 0; i < theLists.length; i++)
            theLists[i].clear();
        currentSize = 0;
    }

    /**
     * A hash routine for String objects.
     * 
     * @param key
     *            the String to hash.
     * @param tableSize
     *            the size of the hash table.
     * @return the hash value.
     */
    public static int hash(String key, int tableSize) {
        int hashVal = 0;

        for (int i = 0; i < key.length(); i++)
            hashVal = 37 * hashVal + key.charAt(i);

        hashVal %= tableSize;
        if (hashVal < 0)
            hashVal += tableSize;

        return hashVal;
    }

    private void rehash() {
        List<T>[] oldLists = theLists;

        // Create new double-sized, empty table
        theLists = new List[nextPrime(2 * theLists.length)];
        for (int j = 0; j < theLists.length; j++)
            theLists[j] = new LinkedList<>();

        // Copy table over
        currentSize = 0;
        for (List<T> list : oldLists)
            for (T item : list)
                insert(item);
    }

    private int myhash(T x) {
        int hashVal = x.hashCode();

        hashVal %= theLists.length;
        if (hashVal < 0)
            hashVal += theLists.length;

        return hashVal;
    }

    private static final int DEFAULT_TABLE_SIZE = 101;

    /** The array of Lists. */
    private List<T>[] theLists;
    private int currentSize;

    /**
     * Internal method to find a prime number at least as large as n.
     * 
     * @param n
     *            the starting number (must be positive).
     * @return a prime number larger than or equal to n.
     */
    private static int nextPrime(int n) {
        if (n % 2 == 0)
            n++;

        for (; !isPrime(n); n += 2)
            ;

        return n;
    }

    /**
     * Internal method to test if a number is prime. Not an efficient algorithm.
     * 
     * @param n
     *            the number to test.
     * @return the result of the test.
     */
    private static boolean isPrime(int n) {
        if (n == 2 || n == 3)
            return true;

        if (n == 1 || n % 2 == 0)
            return false;

        for (int i = 3; i * i <= n; i += 2)
            if (n % i == 0)
                return false;

        return true;
    }

    // Simple main
    public static void main(String[] args) {
        SeparateChainingHashTable<Integer> H = new SeparateChainingHashTable<>();

        long startTime = System.currentTimeMillis();

        final int NUMS = 2000000;
        final int GAP = 37;

        System.out.println("Checking... (no more output means success)");

        for (int i = GAP; i != 0; i = (i + GAP) % NUMS)
            H.insert(i);
        for (int i = 1; i < NUMS; i += 2)
            H.remove(i);

        for (int i = 2; i < NUMS; i += 2)
            if (!H.contains(i))
                System.out.println("Find fails " + i);

        for (int i = 1; i < NUMS; i += 2) {
            if (H.contains(i))
                System.out.println("OOPS!!! " + i);
        }

        long endTime = System.currentTimeMillis();

        System.out.println("Elapsed time: " + (endTime - startTime));
    }

}
View Code

 

linear probing hash table,注意删除元素后,同一个cluster后面的元素都需要重新hash。

package ADT;

import java.util.LinkedList;
import java.util.Queue;

/*************************************************************************
 * Compilation: javac LinearProbingHashST.java Execution: java
 * LinearProbingHashST
 * 
 * Symbol table implementation with linear probing hash table.
 * 
 * % java LinearProbingHashST 128.112.136.11 208.216.181.15 null
 * 
 * 
 *************************************************************************/

public class LinearProbingHashST<Key, Value> {
    private static final int INIT_CAPACITY = 4;

    private int N; // number of key-value pairs in the symbol table
    private int M; // size of linear probing table
    private Key[] keys; // the keys
    private Value[] vals; // the values

    // create an empty hash table - use 16 as default size
    public LinearProbingHashST() {
        this(INIT_CAPACITY);
    }

    // create linear proving hash table of given capacity
    public LinearProbingHashST(int capacity) {
        M = capacity;
        keys = (Key[]) new Object[M];
        vals = (Value[]) new Object[M];
    }

    // return the number of key-value pairs in the symbol table
    public int size() {
        return N;
    }

    // is the symbol table empty?
    public boolean isEmpty() {
        return size() == 0;
    }

    // does a key-value pair with the given key exist in the symbol table?
    public boolean contains(Key key) {
        return get(key) != null;
    }

    // hash function for keys - returns value between 0 and M-1
    private int hash(Key key) {
        return (key.hashCode() & 0x7fffffff) % M;
    }

    // resize the hash table to the given capacity by re-hashing all of the keys
    private void resize(int capacity) {
        LinearProbingHashST<Key, Value> temp = new LinearProbingHashST<Key, Value>(capacity);
        for (int i = 0; i < M; i++) {
            if (keys[i] != null) {
                temp.put(keys[i], vals[i]);
            }
        }
        keys = temp.keys;
        vals = temp.vals;
        M = temp.M;
    }

    // insert the key-value pair into the symbol table
    public void put(Key key, Value val) {
        if (val == null) {
            delete(key);
            return;
        }

        // double table size if 50% full
        if (N >= M / 2)
            resize(2 * M);

        int i;
        for (i = hash(key); keys[i] != null; i = (i + 1) % M) {
            if (keys[i].equals(key)) {
                vals[i] = val;
                return;
            }
        }
        keys[i] = key;
        vals[i] = val;
        N++;
    }

    // return the value associated with the given key, null if no such value
    public Value get(Key key) {
        for (int i = hash(key); keys[i] != null; i = (i + 1) % M)
            if (keys[i].equals(key))
                return vals[i];
        return null;
    }

    // delete the key (and associated value) from the symbol table
    public void delete(Key key) {
        if (!contains(key))
            return;

        // find position i of key
        int i = hash(key);
        while (!key.equals(keys[i])) {
            i = (i + 1) % M;
        }

        // delete key and associated value
        keys[i] = null;
        vals[i] = null;

        // rehash all keys in same cluster
        i = (i + 1) % M;
        while (keys[i] != null) {
            // delete keys[i] an vals[i] and reinsert
            Key keyToRehash = keys[i];
            Value valToRehash = vals[i];
            keys[i] = null;
            vals[i] = null;
            N--;
            put(keyToRehash, valToRehash);
            i = (i + 1) % M;
        }

        N--;

        // halves size of array if it's 12.5% full or less
        if (N > 0 && N <= M / 8)
            resize(M / 2);

        assert check();
    }

    // return all of the keys as in Iterable
    public Iterable<Key> keys() {
        Queue<Key> queue = new LinkedList<Key>();
        for (int i = 0; i < M; i++)
            if (keys[i] != null)
                queue.add(keys[i]);
        return queue;
    }

    // integrity check - don't check after each put() because
    // integrity not maintained during a delete()
    private boolean check() {

        // check that hash table is at most 50% full
        if (M < 2 * N) {
            System.err.println("Hash table size M = " + M + "; array size N = " + N);
            return false;
        }

        // check that each key in table can be found by get()
        for (int i = 0; i < M; i++) {
            if (keys[i] == null)
                continue;
            else if (get(keys[i]) != vals[i]) {
                System.err.println("get[" + keys[i] + "] = " + get(keys[i]) + "; vals[i] = " + vals[i]);
                return false;
            }
        }
        return true;
    }

    /***********************************************************************
     * Unit test client.
     ***********************************************************************/
    public static void main(String[] args) {
        LinearProbingHashST<String, Integer> st = new LinearProbingHashST<String, Integer>();

    }
}
View Code

 

quadratic probing hash table.

package ADT;

// QuadraticProbing Hash table class
//
// CONSTRUCTION: an approximate initial size or default of 101
//
// ******************PUBLIC OPERATIONS*********************
// bool insert( x )       --> Insert x
// bool remove( x )       --> Remove x
// bool contains( x )     --> Return true if x is present
// void makeEmpty( )      --> Remove all items

/**
 * Probing table implementation of hash tables. Note that all "matching" is
 * based on the equals method.
 * 
 * @author Mark Allen Weiss
 */
public class QuadraticProbingHashTable<T> {
    /**
     * Construct the hash table.
     */
    public QuadraticProbingHashTable() {
        this(DEFAULT_TABLE_SIZE);
    }

    /**
     * Construct the hash table.
     * 
     * @param size
     *            the approximate initial size.
     */
    public QuadraticProbingHashTable(int size) {
        allocateArray(size);
        doClear();
    }

    /**
     * Insert into the hash table. If the item is already present, do nothing.
     * 
     * @param x
     *            the item to insert.
     */
    public boolean insert(T x) {
        // Insert x as active
        int currentPos = findPos(x);
        if (isActive(currentPos))
            return false;

        array[currentPos] = new HashEntry<>(x, true);
        theSize++;

        // Rehash; see Section 5.5
        if (++occupied > array.length / 2)
            rehash();

        return true;
    }

    /**
     * Expand the hash table.
     */
    private void rehash() {
        HashEntry<T>[] oldArray = array;

        // Create a new double-sized, empty table
        allocateArray(2 * oldArray.length);
        occupied = 0;
        theSize = 0;

        // Copy table over
        for (HashEntry<T> entry : oldArray)
            if (entry != null && entry.isActive)
                insert(entry.element);
    }

    /**
     * Method that performs quadratic probing resolution.
     * 
     * @param x
     *            the item to search for.
     * @return the position where the search terminates.
     */
    private int findPos(T x) {
        int offset = 1;
        int currentPos = myhash(x);

        while (array[currentPos] != null && !array[currentPos].element.equals(x)) {
            currentPos += offset; // Compute ith probe
            offset += 2;
            if (currentPos >= array.length)
                currentPos -= array.length;
        }

        return currentPos;
    }

    /**
     * Remove from the hash table.
     * 
     * @param x
     *            the item to remove.
     * @return true if item removed
     */
    public boolean remove(T x) {
        int currentPos = findPos(x);
        if (isActive(currentPos)) {
            array[currentPos].isActive = false;
            theSize--;
            return true;
        } else
            return false;
    }

    /**
     * Get current size.
     * 
     * @return the size.
     */
    public int size() {
        return theSize;
    }

    /**
     * Get length of internal table.
     * 
     * @return the size.
     */
    public int capacity() {
        return array.length;
    }

    /**
     * Find an item in the hash table.
     * 
     * @param x
     *            the item to search for.
     * @return the matching item.
     */
    public boolean contains(T x) {
        int currentPos = findPos(x);
        return isActive(currentPos);
    }

    /**
     * Return true if currentPos exists and is active.
     * 
     * @param currentPos
     *            the result of a call to findPos.
     * @return true if currentPos is active.
     */
    private boolean isActive(int currentPos) {
        return array[currentPos] != null && array[currentPos].isActive;
    }

    /**
     * Make the hash table logically empty.
     */
    public void makeEmpty() {
        doClear();
    }

    private void doClear() {
        occupied = 0;
        for (int i = 0; i < array.length; i++)
            array[i] = null;
    }

    private int myhash(T x) {
        int hashVal = x.hashCode();

        hashVal %= array.length;
        if (hashVal < 0)
            hashVal += array.length;

        return hashVal;
    }

    private static class HashEntry<T> {
        public T element; // the element
        public boolean isActive; // false if marked deleted

        public HashEntry(T e) {
            this(e, true);
        }

        public HashEntry(T e, boolean i) {
            element = e;
            isActive = i;
        }
    }

    private static final int DEFAULT_TABLE_SIZE = 101;

    private HashEntry<T>[] array; // The array of elements
    private int occupied; // The number of occupied cells
    private int theSize; // Current size

    /**
     * Internal method to allocate array.
     * 
     * @param arraySize
     *            the size of the array.
     */
    private void allocateArray(int arraySize) {
        array = new HashEntry[nextPrime(arraySize)];
    }

    /**
     * Internal method to find a prime number at least as large as n.
     * 
     * @param n
     *            the starting number (must be positive).
     * @return a prime number larger than or equal to n.
     */
    private static int nextPrime(int n) {
        if (n % 2 == 0)
            n++;

        for (; !isPrime(n); n += 2)
            ;

        return n;
    }

    /**
     * Internal method to test if a number is prime. Not an efficient algorithm.
     * 
     * @param n
     *            the number to test.
     * @return the result of the test.
     */
    private static boolean isPrime(int n) {
        if (n == 2 || n == 3)
            return true;

        if (n == 1 || n % 2 == 0)
            return false;

        for (int i = 3; i * i <= n; i += 2)
            if (n % i == 0)
                return false;

        return true;
    }

    // Simple main
    public static void main(String[] args) {
        QuadraticProbingHashTable<String> H = new QuadraticProbingHashTable<>();

        long startTime = System.currentTimeMillis();

        final int NUMS = 2000000;
        final int GAP = 37;

        System.out.println("Checking... (no more output means success)");

        for (int i = GAP; i != 0; i = (i + GAP) % NUMS)
            H.insert("" + i);
        for (int i = GAP; i != 0; i = (i + GAP) % NUMS)
            if (H.insert("" + i))
                System.out.println("OOPS!!! " + i);
        for (int i = 1; i < NUMS; i += 2)
            H.remove("" + i);

        for (int i = 2; i < NUMS; i += 2)
            if (!H.contains("" + i))
                System.out.println("Find fails " + i);

        for (int i = 1; i < NUMS; i += 2) {
            if (H.contains("" + i))
                System.out.println("OOPS!!! " + i);
        }

        long endTime = System.currentTimeMillis();

        System.out.println("Elapsed time: " + (endTime - startTime));
        System.out.println("H size is: " + H.size());
        System.out.println("Array size is: " + H.capacity());
    }

}
View Code

 

posted @ 2014-07-14 00:37  jdflyfly  阅读(741)  评论(0编辑  收藏  举报