您的位置:首页 > 其它

算法_散列表

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

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

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

  拉链法对于碰撞处理的解决方法是将大小为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;
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: