Java实现散列表/哈希表

概念

散列集数是在记录的存储位置和它的关键字之间建立一个确立的对应关系f,使得每个关键字key对应一个存储位置f(key)。查找时,根据中国确定的读音关系找到给定值key的映射f(key),若查找集合中存在这个记录,则必定在f(key)的位置上。
我们把这种对应关系f称为散列函数,又称为哈希(Hash)函数。按这个思想,采用散列技术将记录存储在一块连续的存储空间中,这块连续的存储空间称为散列表或哈希表。关键字对应的记录存储位置我们称为散列地址。

散列函数构造方法

1 直接定址法

取关键字或关键字的某个线性函数值为散列地址。即f(key) = a * key + b(a、b为常数)。若f(key)已经有值,就往下一个找,直到f(key)中没有值,放进去。

2 数字分析法

找出数字的规律,尽可能利用这些数据来构造冲突几率较低的散列地址。

3 平方取中法

先求出关键字的平方值,然后按需要取平方值的中间几位作为哈希地址。平方后中间几位和关键字中每一位都相关,故不同关键字会以较高的概率产生不同的哈希地址。

4 折叠法

折叠法是将关键字从左到右分割成位数相等的几部分(最后一部分分位数不够时可以短一些),然后将这几个部分叠加求和,并散列表表长,取后几位作为散列地址。

5 除留余数法

此方法为最常用的构造散列函数方法。对于散列表长为m的散列函数公式为:f(key) = key mod p(p<=m)。根据前辈的经验,若散列表表长为m,通常p为小于或等于表长(最好接近于m)的最小质数或不包含小于20质因子的合数

6 随机数法

选择一个随机数,取关键字的随机函数作为它的散列地址。也就是f(key) = random(key)。random是随机函数,当关键字的长度不等式,采用这个方法构造散列函数比较合适。
现实中,应该视不同的情况采取不同的散列函数,参考如下:

  • 计算散列地址所需的时间
  • 关键字的长度
  • 散列表的大小
  • 关键字的分布情况
  • 记录查找的频率

处理散列冲突的方法

1 开放定址法

所谓的开放定址法就是一旦发生了冲突,就去寻找下一个空的散列地址,只要散列表足够大,空的散列地址总能找到,并将记录存入。
公式一:fi(key) = (f(key) + di) MOD m (di = 1,2,3,···,m-1)。这种解决冲突的开放定址法称为线性探测法。
公式二:fi(key) = (f(key) + di) MOD m (di = 12,-12,22,-22,···,q2,-q2, q <= m/2)。增加平方运算的目的是为了不让关键字都聚集在某一块区域。这种方法称作二次探测法。
公式三:fi(key) = (f(key) + di) MOD m (di是随机数列)。冲突时,对于位移量di采用随机函数计算得到,称作随机探测法。

2 再散列函数法

事先准备多个散列函数:fi(key) = RHi(key) (i = 1,2,···k),这里的RHi就是不同的散列函数。每当发生散列地址冲突时就换一个函数计算,相信总会有一个可以吧冲突解决掉,但是计算时间增加。

3 链地址法

很简单,就是讲所有关键字为同义词的记录存储在一个单链表中。

4 公共溢出区法

冲突的去公共溢出区存放。

Hash Table简单实现

1 数组实现

数组为数据结构+除留余数法+开放定址法

import java.util.Random;

class HashTable{
     
    //Hash函数
    private Object[] table1;//数组形式
    //Hash函数
    public int HashCode(Object obj){
     
        int pos = obj.hashCode()%table1.length;
        return Math.abs(pos);
    }
    //HashTables数组构造器
    public HashTable(int len){
     
        this.table1 = new Object[len];
    }
    public int length(){
     
        return table1.length;
    }
    public void Insert(Object obj){
     
        int hashkey = HashCode(obj);
        while(this.table1[hashkey] != null)
            hashkey = (hashkey + 1) % table1.length;
        this.table1[hashkey] = obj;
    }
    public boolean Search(Object obj){
     
        int key = HashCode(obj);
        while(this.table1[key] != null){
     
            if(this.table1[key] == obj)
                return true;
            key = (key + 1) % table1.length;
        }
        return false;
    }
    public void Display(){
     
        for(Object t : table1)
            System.out.print(t + " ");
        System.out.println();
    }
}
public class Study{
     
    public static void main(String[] args) {
     
        HashTable hashTable = new HashTable(10);
        Random rand = new Random();
        System.out.println(hashTable.length());
        for(int i = 0; i < 10; i++){
     
            hashTable.Insert(rand.nextInt(10));
        }
        hashTable.Display();
        System.out.println(hashTable.Search(5));
    }
}
//某一次随机输出为
10
0 6 8 3 3 5 5 7 7 3 
true

2 链表实现

单链表数据结构+除留余数法+链地址法

class HashLink{
     
    private class Node{
     
        private Object data;
        private Node next;
        public Node(Object data, Node next){
     
            this.data = data;
            this.next = next;
        }
        public Node(Object data){
     
            this(data, null);
        }
    }
    private Node[] list;
    public HashLink(int len){
     
        list = new Node[len];
    }
    public void create(int len){
     
        for(int i = 0; i < len; i++)
            list[i] = new Node(null);
    }
    public int HashCode(Object obj){
     
        return Math.abs(obj.hashCode()%list.length);
    }
    public void addHead(int key, Object data){
     
        Node p = new Node(data);
        p.next = this.list[key].next;
        this.list[key].next = p;
    }
    public void Insert(Object data){
     
        int key = HashCode(data);
        addHead(key, data);
    }
    public boolean Search(Object data){
     
        int key = HashCode(data);
        Node p = this.list[key];
        while(p != null){
     
            if(p.data == data)
                return true;
            p = p.next;
        }
        return false;
    }
    public void Display(int index){
     
        Node p = this.list[index].next;
        while(p!=null){
     
            System.out.print(p.data + "->");
            p = p.next;
        }
        System.out.println("null");
    }
    public void toDisplay(){
     
        for(int i = 0; i < list.length; i++){
     
            System.out.print("Key = " + i + ":");
            Display(i);
        }
    }
}
public class Study{
     
    public static void main(String[] args) {
     
        HashLink list = new HashLink(10);
        list.create(10);
        for(int i = 0; i < 25; i++)
            list.Insert(i+1);
        System.out.println(list.Search(25));
        System.out.println(list.Search(35));
        list.toDisplay();
    }
}
//输出如下
true
false
Key = 0:20->10->null
Key = 1:21->11->1->null
Key = 2:22->12->2->null
Key = 3:23->13->3->null
Key = 4:24->14->4->null
Key = 5:25->15->5->null
Key = 6:16->6->null
Key = 7:17->7->null
Key = 8:18->8->null
Key = 9:19->9->null

你可能感兴趣的:(数据结构)