哈希表总结

来源:互联网 发布:mobi for mac 编辑:程序博客网 时间:2024/06/13 12:12

1. 哈希表和哈希地址定义

解析:根据设定的哈希函数H(key)和处理冲突的方法将一组关键字映像到一个有限的连续的地址集(区间)上,并以

键字在地址集中的“像”作为记录在表中的存储位置,这种表便称为哈希表,这一映像过程称为哈希造表或散列,所

得存储位置称哈希地址或散列地址。


2. 哈希函数构造方法

解析:

(1)直接定址法:取关键字或关键字的某个线性函数值为哈希地址。即。其中,a和

b为常数。

(2)数字分析法:假设关键字是以为基的数,并且哈希表中可能出现的关键字都是事先确定的,则可取关键字的若

干数位组成哈希地址。

(3)平方取中法:取关键字平方后的中间几位为哈希地址。这是一种较常用的构造哈希函数的方法。通常在选定哈

希函数时不一定能知道关键字的全部情况,取其中哪几位也不一定合适,而一个数平方后的中间几位数和数的每一位

都相关,由此使随机分布的关键字得到的哈希地址也是随机的。取的位数由表长决定。

(4)折叠法:将关键字分割成位数相同的几部分(最后一部分的位数可以不同),然后取这几部分的叠加和(舍去

进位)作为哈希地址,这方法称为折叠法。在折叠法中数位叠加可以有移位叠加和间界叠加两种方法。移位叠加是将

分割后的每一部分的最低位对齐,然后相加;间界叠加是从一端向另一端沿分割界来回折叠,然后对齐相加。关键字

位数很多,而且关键字中每一位上数字分布大致均匀时,可以采用折叠法得到哈希地址。

(5)除留余数法:取关键字被某个不大于哈希表表长m的树p除后所得余数为哈希地址。即

。除关键字直接取模外,也可在折叠、平方取中等运算之后取模。

(6)随机数法:选择一个随机函数,取关键字的随机函数值为它的哈希地址,即H(key)=random(key),其中random

为随机函数。通常,当关键字长度不等时采用此法构造哈希函数较恰当。

说明:构造哈希函数需要考虑的因素:计算哈希函数所需时间(包括硬件指令的因素);关键字的长度;哈希表的大

小;关键字的分布情况;记录的查找频率。


3. 处理哈希地址冲突方法

解析:

(1)开放定址法:。其中,H(key)为哈希函数;m为哈希表表

长;为增量序列。(1),称线性探测再散列;(2)

称二次探测再散列;(3)伪随机数序列,称伪随机探测再散列。

(2)再哈希法:均是不同的哈希函数,即在同义词产生地址冲突时计算另一个哈

希函数地址,直到冲突不再发生。这种方法不易产生“聚集”,但增加了计算的时间。

(3)链地址法:将所有关键字为同义词的记录存储在同一线性链表中。假设某哈希函数产生的哈希地址在区间[0,m-

1]上,则设立一个指针型向量Chain ChainHash[m];其每个分量的初始状态都是空指针。凡哈希地址为i的记录都插入

到头指针为ChainHash[i]的链表中。在链表中的插入位置可以在表头或表尾;也可以在中间,以保持同义词在同一线

性链表中按关键字有序。

(4)建立一个公共溢出区:假设哈希函数的值域为[0,m-1],则设向量HashTable[0..m-1]为基本表,每个分量存放一

个记录,令设立向量OverTable[0..v]为溢出表。所有关键字和基本表中关键字为同义词的记录,不管它们由哈希函数

得到的哈希地址是什么,一旦发生冲突,都填入溢出表。


3. 链地址法

(1)链地址法数据结构

public class SeparateChainingHashST<Key, Value> {    private static final int INIT_CAPACITY = 4;    private int n; // number of key-value pairs    private int m; // hash table size    private SequentialSearchST<Key, Value>[] st; // array of linked-list symbol tables    ......}

说明:n表示键值对总数,m表示散列表的大小,st表示存放链表对象的数组。

(2)插入操作

public void put(Key key, Value val) {    if (key == null) throw new IllegalArgumentException("first argument to put() is null");    if (val == null) {        delete(key);        return;    }    // double table size if average length of list >= 10    if (n >= 10 * m) resize(2 * m);    int i = hash(key);    if (!st[i].contains(key)) n++;    st[i].put(key, val);}
(3)查找操作
public Value get(Key key) {    if (key == null) throw new IllegalArgumentException("argument to get() is null");    int i = hash(key);    return st[i].get(key);}
(4)删除操作
public void delete(Key key) {    if (key == null) throw new IllegalArgumentException("argument to delete() is null");    int i = hash(key);    if (st[i].contains(key)) n--;    st[i].delete(key);    // halve table size if average length of list <= 2    if (m > INIT_CAPACITY && n <= 2 * m) resize(m / 2);}
(5)重置哈希表大小
private void resize(int chains) {    SeparateChainingHashST<Key, Value> temp = new SeparateChainingHashST<Key, Value>(chains);    for (int i = 0; i < m; i++) {        for (Key key : st[i].keys()) {            temp.put(key, st[i].get(key));        }    }    this.m = temp.m;    this.n = temp.n;    this.st = temp.st;}


4. 线性探测法

(1)线性探测法数据结构

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    ......}

说明:n表示符号表中键值对的总数,m表示线性探测表的大小,keys表示键,vals表示值。

(2)插入操作

public void put(Key key, Value val) {    if (key == null) throw new IllegalArgumentException("first argument to put() is null");    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++;}
(3)查找操作
public Value get(Key key) {    if (key == null) throw new IllegalArgumentException("argument to get() is null");    for (int i = hash(key); keys[i] != null; i = (i + 1) % m)        if (keys[i].equals(key))            return vals[i];    return null;}
(4)删除操作
public void delete(Key key) {    if (key == null) throw new IllegalArgumentException("argument to delete() is null");    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();}
(5)重置哈希表大小
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;}


参考文献:

[1] 数据结构(C语言版)

[2] 算法(第4版) 

原创粉丝点击