数据结构之Hash(java语言版)

Hash表

Hash也叫散列、哈希,是一种根据key-value对进行存储的数据结构。每个value对应一个key,这样查找的时候就无需遍历。

Hash表使用数组作为底层结构,数组中每个区域都存储着Hash,这就是Hash表。

在这里插入图片描述

列表、数组、树这些数据结构在查询数据时的时间复杂度通常为O(n),而Hash的时间复杂度为O(1).

Hash函数:哈希表的键值之间必须有个映射关系,这个关系就是hash函数,如key=f(value).Hash的优劣取决于Hash函数。

哈希碰撞

如果不同的value的key一样就会造成hash碰撞。

比如我们选择的Hash函数为取余函数,以10为取余单位,11和1取到的余数是一样的,

如果我们用余数作为key,那就会引发hash碰撞。

解决方法

开放地址法:让相同的key再找一块儿地方待着,比如1的key待在数组的下标1索引的位置上,11的key待在其他位置,比如待在2的位置(前提是2这里没其他人)。

​ 链地址法:在value的地址上加一个链表,一个个连在一起

实例

通过实例实现一下,选取取余函数作为Hash函数,余数充当key,我们使数组的每个下标对应一个key。

如11的余数是1,则11的key就是1,那11就存储在数组中下标为1的位置。

如果出现冲突,使用开放地址法解决Hash冲突。

数据单位:

class DataItem{//数据项
    private int iData;
    public DataItem(int iData){
        this.iData = iData;
    }
    public int getKey(){
        return iData;
    }
}

取余函数

取余函数的被余数使用数组长度就好。

 //通过哈希函数转换得到数组下标
    public int hashFunction(int key){
        return key%arraySize;
    }

相关结构

数据成员,构造函数以及基础方法。

	private DataItem[] hashArray;   //DataItem类,表示每个数据项信息
    private int arraySize;//数组的初始大小
    private int itemNum;//数组实际存储了多少项数据
    private DataItem nonItem;//用于删除数据项
     
    public Hash(int arraySize){//构造方法
        this.arraySize = arraySize;
        hashArray = new DataItem[arraySize];
        nonItem = new DataItem(-1);//删除的数据项下标为-1
    }
    //判断数组是否存储满了
    public boolean isFull(){
        return (itemNum == arraySize);
    }
     
    //判断数组是否为空
    public boolean isEmpty(){
        return (itemNum == 0);
    }


	//打印数组内容
    public void display(){
        System.out.println("data:");
        for(int j = 0 ; j < arraySize ; j++){
            if(hashArray[j] != null){
                System.out.print(hashArray[j].getKey() + " ");
            }else{
                System.out.print("** ");
            }
        }
    }

   

删除、查找

   //删除数据项
    public DataItem delete(int key){
        if(isEmpty()){
            System.out.println("Hash Table is Empty!");
            return null;
        }
        int hashVal = hashFunction(key);
        while(hashArray[hashVal] != null){
            if(hashArray[hashVal].getKey() == key){
                DataItem temp = hashArray[hashVal];
                hashArray[hashVal] = nonItem;//nonItem表示空Item,其key为-1
                itemNum--;
                return temp;
            }
            ++hashVal;
            hashVal %= arraySize;
        }
        return null;
    }
     
    //查找数据项
    public DataItem find(int key){
        int hashVal = hashFunction(key);
        while(hashArray[hashVal] != null){
            if(hashArray[hashVal].getKey() == key){
                return hashArray[hashVal];
            }
            ++hashVal;
            hashVal %= arraySize;
        }
        return null;
    }

插入

    //插入数据项
    public void insert(DataItem item){
        if(isFull()){
            //扩展哈希表
            System.out.println("哈希表已满,重新哈希化...");
            extendHashTable();
        }
        int key = item.getKey();
        int hashVal = hashFunction(key);
        while(hashArray[hashVal] != null && hashArray[hashVal].getKey() != -1){
            ++hashVal;
            hashVal %= arraySize;
        }
        hashArray[hashVal] = item;
        itemNum++;
    }
  

扩容

数组有固定的大小,而且不能扩展,所以扩展哈希表只能另外创建一个更大的数组,然后把旧数组中的数据插到新的数组中。
但是哈希表是根据数组大小计算给定数据的位置的,所以这些数据项不能再放在新数组中和老数组相同的位置上。

因此不能直接拷贝,需要按顺序遍历老数组,并使用insert方法向新数组中插入每个数据项。
这个过程叫做重新哈希化。这是一个耗时的过程,但如果数组要进行扩展,这个过程是必须的。

 
    public void extendHashTable(){
        int num = arraySize;
        itemNum = 0;//重新计数,因为下面要把原来的数据转移到新的扩张的数组中
        arraySize *= 2;//数组大小翻倍
        DataItem[] oldHashArray = hashArray;
        hashArray = new DataItem[arraySize];
        for(int i = 0 ; i < num ; i++){
            insert(oldHashArray[i]);
        }
    }

完整实现

public class Hash{

    private DataItem[] hashArray;   //DataItem类,表示每个数据项信息
    private int arraySize;//数组的初始大小
    private int itemNum;//数组实际存储了多少项数据
    private DataItem nonItem;//用于删除数据项
     
    public Hash(int arraySize){
        this.arraySize = arraySize;
        hashArray = new DataItem[arraySize];
        nonItem = new DataItem(-1);//删除的数据项下标为-1
    }
    //判断数组是否存储满了
    public boolean isFull(){
        return (itemNum == arraySize);
    }
     
    //判断数组是否为空
    public boolean isEmpty(){
        return (itemNum == 0);
    }
     
    //打印数组内容
    public void display(){
        System.out.println("data:");
        for(int j = 0 ; j < arraySize ; j++){
            if(hashArray[j] != null){
                System.out.print(hashArray[j].getKey() + " ");
            }else{
                System.out.print("** ");
            }
        }
    }
    //通过哈希函数转换得到数组下标
    public int hashFunction(int key){
        return key%arraySize;
    }
     
    //插入数据项
    public void insert(DataItem item){
        if(isFull()){
            //扩展哈希表
            System.out.println("哈希表已满,重新哈希化...");
            extendHashTable();
        }
        int key = item.getKey();
        int hashVal = hashFunction(key);
        while(hashArray[hashVal] != null && hashArray[hashVal].getKey() != -1){
            ++hashVal;
            hashVal %= arraySize;
        }
        hashArray[hashVal] = item;
        itemNum++;
    }

    public void extendHashTable(){
        int num = arraySize;
        itemNum = 0;//重新计数,因为下面要把原来的数据转移到新的扩张的数组中
        arraySize *= 2;//数组大小翻倍
        DataItem[] oldHashArray = hashArray;
        hashArray = new DataItem[arraySize];
        for(int i = 0 ; i < num ; i++){
            insert(oldHashArray[i]);
        }
    }
     
    //删除数据项
    public DataItem delete(int key){
        if(isEmpty()){
            System.out.println("Hash Table is Empty!");
            return null;
        }
        int hashVal = hashFunction(key);
        while(hashArray[hashVal] != null){
            if(hashArray[hashVal].getKey() == key){
                DataItem temp = hashArray[hashVal];
                hashArray[hashVal] = nonItem;//nonItem表示空Item,其key为-1
                itemNum--;
                return temp;
            }
            ++hashVal;
            hashVal %= arraySize;
        }
        return null;
    }
     
    //查找数据项
    public DataItem find(int key){
        int hashVal = hashFunction(key);
        while(hashArray[hashVal] != null){
            if(hashArray[hashVal].getKey() == key){
                return hashArray[hashVal];
            }
            ++hashVal;
            hashVal %= arraySize;
        }
        return null;
    }


}

测试方法

 public static void main(String[] args) {
        Hash hash=new Hash(10);
        hash.insert(new DataItem(11));
        hash.insert(new DataItem(8));
        hash.insert(new DataItem(1));
        hash.insert(new DataItem(123));
        hash.insert(new DataItem(39));
        hash.insert(new DataItem(13));

        hash.display();

      
    }

结果:

data:
** 11 1 123 13 ** ** ** 8 39

posted @ 2024-04-11 11:46  cgl_dong  阅读(44)  评论(0编辑  收藏  举报