java数据结构-哈希函数的构造方法

来源:互联网 发布:dota视频站软件 编辑:程序博客网 时间:2024/06/06 00:54

一、哈希函数是什么

哈希法又称散列法,杂凑法、关键字地址计算法,利用哈希法所构造出来的表叫做哈希表。哈希法的基本思想就是,在key和value的储存位置p之间构造一个哈希函数H,使得H(key)=p,H就称之为哈希函数。当创建哈希表时,直接把关键字为Key的元素存入H(key)的单元,下一次查找时就可以直接通过H(key)来计算出关键字为key的储存位置从而找到关键字为key的元素。

二、哈希函数的构造方法

常用的hash函数的构造方法有:数字分析法,平方取中法,分段叠加法、除留余数法、伪随机数法。

1、数字分析法

某学校有学生1000人,学生的学号为10086000-10086999之间,我们要把学生的基本信息和学号储存到hash表中然后只需要输入学号就能查找出相应的信息。对于这个案列来说,首先表的长度与关键字的集合我们都已经确定了,并且关键字分布均匀,那么就可以使用数字分析法来构造哈希函数,通过取学号的后3位来作为哈希函数的key。
/** * Created by zhu on 2017/3/6. * 储存key-value的单位 */public class Node {    int hash;    Object key;    Object value;    Node next;    public Node(Object key, Object value,int hash) {        this.key = key;        this.value = value;        this.hash = hash;    }    public Object getKey() {        return key;    }    public Object getValue() {        return value;    }    public Node getNext() {        return next;    }    public void setNext(Node next) {        this.next = next;    }    public void setValue(Object value) {        this.value = value;    }    public int getHash() {        return hash;    }}

/** * 数字分析法:假设key为10086000-10086999之间的数字 ,key的位数值就取数字的后三位 * */public class DigitalAnalysis implements HashConstruct{     int defaultSize = 1000;     Node[] table;    public DigitalAnalysis() {    }    @Override    public int construct() {        return 0;    }    @Override    public int hash(Object key) {        int intKey = (int) key;        return intKey-10086000;    }    @Override    public void put(Object key, Object value) {        if (table == null){            table = new Node[defaultSize];        }        int index = hash(key);        Node node = new Node(key, value,index);        table[index] = node;    }    @Override    public Object get(Object key) {        int index = hash(key);        return table[index].getValue();    }}

/** * Hash测试客户端 */public class Client {    public static void main(String[] args) {//        数字分析法        DigitalAnalysis digitalAnalysis = new DigitalAnalysis();        digitalAnalysis.put(10086111,"旭旭宝宝");        System.out.println(digitalAnalysis.get(10086111));    }}

运行结果:


以上例子就是哈希函数的构造方法数据分析法,数字分析法只能应用于我们事先知道关键字的集合,并且关键字集合分布均匀的时候才能使用这方法构造哈希函数。

2、平法取中法

对于关键字分布不均匀的情况下,我们可以通过把关键字的平方之后然后取平方后的其中几位数作为key值,这样的话就可以保证哈希函数有较高的概率产生不同的hash值。
某工厂大约个工人,每个工人的名字都是不一样的,现在需要通过建立哈希表来通过工人的姓名来查找出每个工人的工资,这里我们通过平方取中法来构造哈希表。
/** * 平方取中法 ,取数值的ascii编码,平方后取7 - 12位 */public class SquareTakeLaw implements HashConstruct{    int defaultSize = 100000;    Node[] table;    @Override    public int construct() {        return 0;    }    @Override    public int hash(Object key) {        char[] keyCharArr = ((String) key).toCharArray(); //获取key的ascii编码集        StringBuffer stringBuffer = new StringBuffer();        for (char iiem :keyCharArr){            stringBuffer.append((int)iiem);        }        Long charLong = Long.decode(stringBuffer.toString());        charLong = charLong*charLong;        return Integer.parseInt(charLong.toString().substring(7,12));//取平方后取7 - 12位    }    @Override    public void put(Object key, Object value) {        if (table == null){            table = new Node[defaultSize];        }        int index = hash(key);        Node node = new Node(key, value,index);        table[index] = node;    }    @Override    public Object get(Object key) {        int index = hash(key);        return table[index].getValue();    }}
测试客户端:
/** * Hash测试 */public class Client {   //        平方取中法        HashConstruct squareTakeLaw = new SquareTakeLaw();        squareTakeLaw.put("张伟","1000元");        squareTakeLaw.put("张大伟","1200元");        squareTakeLaw.put("alex","1500元");        System.out.println("张伟: "+squareTakeLaw.get("张伟"));        System.out.println("张大伟: "+squareTakeLaw.get("张大伟"));        System.out.println("alex "+squareTakeLaw.get("alex"));    }}


运行结果:




3.分段叠加法

这种方法是将key分为位数相等的几部分进行相加,如下所示,最终结果1105,舍弃最高位也就是取相加的位数最终为105
1 2 3
6 0 3
2 4 7
1 1 2
0 2 0
---------------
     1 1 0 5
同样的是上面的例子:某工厂大约100000个工人,每个工人的名字都是不一样的,现在需要通过建立哈希表来通过工人的姓名来查找出每个工人的工资。我们通过分段叠加法来构造哈希表。
/** * 分段叠加法 取5位数进行相加 */public class SegmentedOverlay implements HashConstruct{    int defaultSize = 100000;    Node[] table;    @Override    public int construct() {        return 0;    }    @Override    public int hash(Object key) {        char[] keyCharArray = ((String) key).toCharArray(); //获取key的ascii编码集        StringBuffer stringBuffer = new StringBuffer();        int hash = 0;        for (char item : keyCharArray) {            stringBuffer.append((int) item);        }        //取keykey的ascii编码集的每5位进行相加,并取结果的后5位        String ovelayString = stringBuffer.substring(0, stringBuffer.length() - stringBuffer.length() % 5);        for (int i = 5; i <= ovelayString.length(); i = i + 5) {            hash = hash + Integer.parseInt(ovelayString.substring(i - 5, i));        }        hash = Integer.decode(String.valueOf(hash).substring(String.valueOf(hash).length() - 5, String.valueOf(hash).length()));        return hash;    }    @Override    public void put(Object key, Object value) {        if (table == null) {            table = new Node[defaultSize];        }        int index = hash(key);        Node node = new Node(key, value,index);        if (index == -1) {            return;        }        table[index] = node;    }    @Override    public Object get(Object key) {        int index = hash(key);        if (index == -1){            return null;        }        return table[index].getValue();    }}

测试客户端:
/** * Hash测试 */public class Client {   //        平方取中法           HashConstruct hashConstruct = new SegmentedOverlay();        hashConstruct.put("张伟","1000元");        hashConstruct.put("张大伟","1200元");        hashConstruct.put("alex","1500元");        System.out.println("张伟: "+hashConstruct.get("张伟"));        System.out.println("张大伟: "+hashConstruct.get("张大伟"));        System.out.println("alex "+hashConstruct.get("alex"));    }}
测试结果:

三、总结

hash表常见的的构造方法,目前就介绍了这3钟 数字分析法,平方取中法,分段叠加法剩下的除留余数法、伪随机数法大家可以百度来看看其他的两种构造方法是怎么实现的,上面举得这3个例子所用的代码实现都是最简单的代码实现,对很多情况没有考虑以及设计还不完善,还没有进行hash函数冲突的处理即是当key1 ≠ key2,H(key1)==H(key2)这种冲突进行处理,之后会介绍hash函数如何处理冲突的几种方法。
1 0