算法_散列表

  使用散列的查找算法分为两步,第一步用散列函数将被查找的键转化为数组的一个索引,理想情况下不同的键都被转化为不同的索引值.而当多个键散列到相同的索引值的情况下,就需要处理碰撞冲突,为此有两种方法,拉链法和线性探测法.

  散列函数用于通过键来获取其对应的索引值.好的散列函数应该具有计算简便,等价的键必然产生相等的散列值,均匀的散列所有的键的条件.

  一.基于拉链法的散列表.

  拉链法对于碰撞处理的解决方法是将大小为M的数组中的每个元素都指向一个链表,链表中的每一个节点都存储了散列值为该元素的索引的键值对.查找分两步:首先根据散列值找到对应的链表,然后沿着链表顺序查找相应的键.用M条链表保存N个键,链表的平均长度为N/M.代码实现如下:

public class SeparateChainingHashST<Key,Value> {
    private int N;            //键值对总数
    private int M;            //散列表大小
    private SequentialSearchST<Key,Value>[] st;
    public SeparateChainingHashST() {
        this(997);
    }
    public SeparateChainingHashST(int M) {
        //创建M条链表.
        this.M=M;
        st=(SequentialSearchST<Key,Value>[])new SequentialSearchST[M];
        for (int i = 0; i < M; i++) {
            st[i]=new SequentialSearchST();//创建链表
        }
    }
    private int hash(Key key) {
        return (key.hashCode()&0x7fffffff)%M;
    }
    public Value get(Key key) {
        return (Value)st[hash(key)].get(key);
    }
    public void put(Key key,Value val) {
        st[hash(key)].put(key, val);
    }
}
//链表
class SequentialSearchST<Key,Value> {
        private Node first;     //链表首节点
        private 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(Key key,Value val) {
            for (Node x=first;x!=null;x=x.next) {
                if(key.equals(x.key)) {
                    x.val=val;
                    return;
                }
                first=new Node(key,val,first);
            }
        }
    }

  二.基于线性探测法的散列表

  实现散列表的第二种形式就是用大小为M的数组保存N个键值对,其中M>N.依靠数组的空位来解决碰撞冲突.当碰撞发生的时候,直接检查散列表的下一个位置,并将索引加1.我们用散列函数找到键在数组的索引,判断键是否和被查找的键相同,如果不同则继续查找,直到找到该键或者遇到一个空元素.代码实现如下(其中需要动态的调整数组的大小以提高查找效率)

public class LinearProbingHashST<Key,Value> {
    private int N;        //符号表中的键值对的总数
    private int M=16;    //线性探测表的大小
    private Key[] keys;    //
    private Value[] vals;//
    public LinearProbingHashST() {
        keys=(Key[]) new Object[M];
        vals=(Value[]) new Object[M];
    }
    public LinearProbingHashST(int x) {
        M=x;
        keys=(Key[]) new Object[M];
        vals=(Value[]) new Object[M];
    }
    private int hash(Key key) {
        return (key.hashCode()&0x7fffffff)%M;
    }
    private void resize(int cap) {
        LinearProbingHashST<Key, Value> t;
        t=new LinearProbingHashST<>(cap);
        for(int i=0;i<M;i++) {
            if(keys[i]!=null)
                t.put(keys[i], vals[i]);
        }
        keys=t.keys;
        vals=t.vals;
        M=t.M;
    }
    //实现动态的调整数组大小
    public void put(Key key,Value val) {
        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++;
    }
    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;
    }
    /*
     * 删除元素不能直接设为null
     * 因为会影响到后续元素的查找,方法是将后面的元素重新插入数组.
     * */
    public void delete(Key key) {
        if(!contains(key)) return;
        int i=hash(key);
        while(!key.equals(keys[i]))
            i=(i+1)%M;
        keys[i]=null;
        vals[i]=null;
        i=(i+1)%M;
        while(keys[i]!=null) {
            Key keyToRedo=keys[i];
            Value valToRedo=vals[i];
            keys[i]=null;
            vals[i]=null;
            N--;
            put(keyToRedo,valToRedo);
            i=(i+1)%M;
        }
        N--;
        if(N>0&&N==M/8) resize(M/2);
    }
    public boolean contains(Key key) {
        for(Key okey:keys) {
            if(key.equals(okey))
                return true;
        }
        return false;
    }
}
原文地址:https://www.cnblogs.com/hlhdidi/p/5677432.html