首页 > 编程语言 >数据结构之Hash(java语言版)

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

时间:2024-04-11 11:56:32浏览次数:20  
标签:hashVal Hash hashArray int arraySize key java 语言版 DataItem

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

标签:hashVal,Hash,hashArray,int,arraySize,key,java,语言版,DataItem
From: https://www.cnblogs.com/cgl-dong/p/18128731

相关文章

  • 数据结构之二叉搜索树(java语言版)
    之前介绍了树,主要实现了二叉树的代码。在二叉树的基础上有许多衍生的树,如二叉搜索树、哈夫曼树等,今天学习一下二叉搜索树。二叉搜索树二叉搜索树是二叉树的一种,是应用非常广泛的一种二叉树,英文简称为BST又被称为:二叉查找树、二叉排序树特点任意一个节点的值都大于其左子树所......
  • 数据结构之链表(c语言版)
    链表是线性表,链表的特点就是可以动态增减元素。种类有单向链表、双向链表,循环链表。一、单链表单链表的储存思想使用指针表示节点之间的逻辑关系,它的储存单元可以连续也可以不连续,每个储存单元需要储存信息和储存与后继节点的地址信息,储存单元又称之为节点。单链表由头指针唯......
  • 数据结构之栈(c语言版)
    栈(stack):在逻辑上是一种线性存储结构,它有以下几个特点:1、栈中数据是按照"后进先出(LIFO,LastInFirstOut)"方式进出栈的。2、向栈中添加/删除数据时,只能从栈顶进行操作。栈通常包括的三种操作:push、peek、pop。push--向栈中添加元素。peek--返回栈顶元素。pop--返......
  • 数据结构之队列(c语言版)
    队列(Queue):在逻辑上是一种线性存储结构。它有以下几个特点:1、队列中数据是按照"先进先出(FIFO,First-In-First-Out)"方式进出队列的。2、队列只允许在"队首"进行删除操作,而在"队尾"进行插入操作。队列通常包括的两种操作:入队列和出队列。队列的种类也很多,单向队列,双向队列,循......
  • 数据结构之二叉树(c语言版)
    之前的都是线性结构,而树结构在计算机应用中的应用更加广泛。linux中的目录结构,某些数据库的底层存储等,都是采用树结构进行构架的。树的概念线性表是一对一的关系,而树是一对多的关系。树的结点:包含一个数据元素及若干指向子树的分支;孩子结点:结点的子树的根称为该结点的孩子;双......
  • 数据结构之图(c语言版)
    图是比树更复杂的结构,树是一对多的关系,图是多对多的关系。一、基本概念1、定义:图(graph)是由一些点(vertex)和这些点之间的连线(edge)所组成的;其中,点通常被成为"顶点(vertex)",而点与点之间的连线则被成为"边或弧"(edege)。通常记为,G=(V,E)。2、根据边是否有方向,将图可以划分......
  • 链表(java语言版)
    链表(Linkedlist)是一种常见的基础数据结构,是一种线性表,但是并不会按线性的顺序存储数据,而是在每一个节点里存到下一个节点的指针(Pointer)。使用链表结构可以充分利用计算机内存空间,实现灵活的内存动态管理。但是链表失去了数组随机读取的优点,同时链表由于增加了结点的指针域......
  • 排序算法(c语言版)
    排序算法(c语言版)1、插入排序#include<stdio.h>//插入排序,升序voidinsertion_sort(intarr[],intlen){inti,j,key;for(i=1;i<len;i++){key=arr[i];//arr[i]为待插入的元素,保存在key中j=i-1;wh......
  • 深入浅出 妙用Javascript中apply、call、bind
    这篇文章实在是很难下笔,因为网上相关文章不胜枚举。巧合的是前些天看到阮老师的一篇文章的一句话:“对我来说,博客首先是一种知识管理工具,其次才是传播工具。我的技术文章,主要用来整理我还不懂的知识。我只写那些我还没有完全掌握的东西,那些我精通的东西,往往没有动力写。炫耀从来......
  • java代码将16进制字符串转换为图片,jdbc入库blob字段,解决ORA-01704,PLS-00172,ORA-06550,
    从Oracle导出SQL文件中的insert语句包含blob字段,语句HEXTORAW函数将16进制的字符串入库,由于字符串太长,insert失败下面的代码读取完整的insert语句,将HEXTORAW函数连同16进制的字符串替换为NULL,先将字段置空插入记录,然后使用PreparedStatement对图片文件读流更新入库importorg.......