`
freewxy
  • 浏览: 336675 次
  • 性别: Icon_minigender_1
  • 来自: 上海
社区版块
存档分类
最新评论

使用数组和链表实现hash表存储信息

阅读更多

1、HashTable的原理:

   通过节点的关键码确定节点的存储位置,即给定节点的关键码k,通过一定的函数关系H(散列函数),得到函数值H(k),将此值解释为该节点的存储地址.

<!--EndFragment-->

 简而言之哈希表之所以能够实现根据关键字来获取记录,  是因为它在内部建立了记录存储位置 即内部数组中的索引号和关键字的一套对应关系H因而在查找时只需根据这个映射关系H 找到给定键值 对应的数H(K)就可直接从数组中取得目的数据,而不必对数组进行遍历和比较这个对应关系H我们称为哈希函数.

哈希函数 H的两个重要特点:   
  1>、哈希函数可以自定义只要使得整数 H(K) 的范围不超出哈希表内部存储数组的上下界即可。以下代码即使用了键值index=K%table.length的方法。
    2>、K 的取法有任意种但 H(K) 只能固定在一个范围因此不同的关键字可能对应了相同的哈希值形成了冲突.

<!--EndFragment-->2、

 

3、冲突及解决方案

     需要注意的是哈希函数的运算和冲突的处理都需要系统开销,尤其是后者代价不菲。因此产生了两个关键问题如何设计函数 H的算法以及如何处理冲突才能使得哈希表更加高效。

  冲突解决技术可以分为两类:开散列方法(也称单练方法或者挂链方法)和闭散列方法(也称开地址法)。这两种方法不同之处与冲突记录的存储有关系。开散列方法将冲突记录在表外,即在表外挂上链表,闭散列方法这把冲突记录存储在表中另一个槽(数组)中。<!--EndFragment-->

   开散列方法(挂链法)的一种简单形式把散列表中的每个槽(数组的每个存储地址)定义为一个链表的表头,散列到一个槽的所有记录(即hash索引值相同时)都放在该槽的链表中。以下代码即以这种方式解决了冲突。

   闭散列冲突解决方法以及其他改进的冲突解决方案,这里就不详述了,一般的数据结构书上都有介绍。 

 

4、HashTable综合了数组的高效查询和链表的高效增删改,以下是以数组和链表为基础实现的hash表代码。

个人信息类:Accout_info.java

public class Account_info { 
    private String name; 
    private String password; 
    private String city; 

   public Account_info(String name,String password,String city){ 
        this.name=name; 
        this.password=password; 
        this.city=city; 
    } 

    public String toString(){ 
        return name+" "+password+" "+city; 
    } 
    //get和set函数省略 
}

 

 

信息节点类:Account_node.java

public class Account_Node { 
     public Account_Node(int q_num,Account_info info){ 
        keyQQ=q_num; 
        acc_info=info; 
    } 

    public Account_Node next; 
    public int keyQQ; 
    public Account_info acc_info; 
    public String toString(){ 
         return keyQQ+" "+acc_info.toString(); 
   } 
}

  

Hash表类:HashTable.java

public class HashTable { 
    //基数组,hash表 
    private Account_Node[] table=new Account_Node[50]; 
    //阈值 hash表存储的数据达到阈值,则rehash 
    private float threshold=0.75F; 
    //hash表中数据总数  
    private long len=0; 

    //将账号信息放入hash表中 
    public void put(int q_num,Account_info acc_info){ 
        //通过自定义hash函数获取索引值 
        int index=hashQ_num(q_num); 
       /* 
        * 判断是否超过阈值 
        * 如果超过,则reHash一次,capacity扩大 
        * 然后再放! 
      */ 
       if(isThreshold()){ 
            int oldCapacity=table.length; 
            Account_Node[] oldTab=table; 
            int newCapacity=oldCapacity*2+1;//数组扩容 
            table=new Account_Node[newCapacity]; 
            for(int i=0;i<oldCapacity;i++){ 
               Account_Node node=oldTab[i]; 
               while(node!=null){ 
                     int newIndex=hashQ_num(node.keyQQ); 
                     table[newIndex]=node; 
                     node=node.next; 
               } 
            } 
        } 


        //1、如果hash表中索引值对应的首个地址为空,即不存在冲突 
        if(table[index]==null){ 
            table[index]=new Account_Node(q_num,acc_info); 
        } 
        //2、存在冲突,则将数据节点放入挂链中的最后一个节点后 
        else{ 
            Account_Node node=table[index]; 
           //找到链表node的最后一个节点 
           while(node.next!=null){ 
                node=node.next; 
           } 
          //将账户信息节点插入存在index冲突的链表中的最后一个节点 
           node.next=new Account_Node(q_num,acc_info); 
        } 
        len++; 
    } 

      //从hash表中取出账号信息 
    public Account_Node get(int Q_num){ 
        int index=hashQ_num(Q_num); 
        Account_Node acc_Node=table[index]; 

        //看是否有下了个节点,如有,则是冲突的,就要一个一个比较 
       if(table[index]==null) 
       return null; 
        while(acc_Node.next!=null){ 
           if(acc_Node.keyQQ==Q_num){ 
           return acc_Node; 
           }else{ 
                 acc_Node=acc_Node.next; 
           } 
       } 
        if(acc_Node.keyQQ!=Q_num) 
             return null; 
        return acc_Node; 
    } 

    //显示hash表 
    public void showTable(){ 
        for(int index=0;index<table.length;index++){ 
            System.out.print(index+"\t"); 
            Account_Node node=table[index]; 
            while(node!=null){ 
                System.out.print(node.acc_info.getName()+"\t"); 
                node=node.next; 
           } 
           System.out.println(""); 
        } 
   } 

    //通过账号号码计算账号的has索引值,自定义hash函数 
    private int hashQ_num(int q_num){ 
        int index=(int) (q_num%table.length); 
        return index; 
   } 

   //判断hash表是否达到阈值 
   //己放信息节点的个数和table的长度比 
    private boolean isThreshold(){ 
        if(len/table.length>=threshold) 
        return true ; 
        return false; 
    } 

    //获取存入hash表中信息的数量 
    public long TableLen(){ 
         return this.len;  
    } 
}

  

测试函数:Test.java

public class Test { 

/**测试函数 
* @param args 
*/ 
    public static void main(String[] args) { 
        HashTable hsT=new HashTable(); 
        Account_info acc; 
        for(int i=0;i<200;i++){ 
           String name="Name"+i; 
           String pwd="Pwd"+i; 
           acc=new Account_info(name,pwd,"长沙"); 
           hsT.put(i, acc); 
       } 
       acc=new Account_info("wxy","123","长沙"); 
       hsT.put(108, acc); 
       hsT.showTable(); 
       Account_Node node=hsT.get(50); 
       System.out.println(node); 
       node=hsT.get(120); 
       System.out.println(node); 
   } 
}

 

 

   疏漏偏颇之处还请高手指教!

分享到:
评论
4 楼 alafqq 2016-11-07  
很好的一篇启蒙hashmap的文章;
HASHTABLE的93行写错了;
我想应该是
if(len>table.length*threshold){
3 楼 lujian19861986 2012-08-14  
还是用c写hashTable省空间
2 楼 lujian19861986 2012-08-14  
不错
1 楼 javafound 2010-11-19  
代码不行,排版可以整些 .

相关推荐

    HashMap原理.docx

    HashMap是一个散列桶(数组和链表),它存储的内容是键值对(key-value)映射HashMap采用了数组和链表的数据结构,能在查询和修改方便继承了数组的线性查找和链表的寻址修改HashMap是非synchronized,所以HashMap很快...

    Hash表的分析以及组成原理解析及代码实现.md

    Hash表采用了数组加链表的结构,即一个数组元组中不再是存储单个元素,而是存储一个链表,就好比包租婆收租的时候,一个握把上面挂了一连串的钥匙一样。Hash表的引出是为了减少查询数据库操作所带来的时间问题,将...

    数据结构学习-教程与代码

    数组(Array):连续存储元素的集合,可以通过索引访问元素。插入和删除元素的时间复杂度较高,为 O(n),但...哈希表(Hash Table):使用哈希函数将键映射到存储位置的数据结构,可以实现快速的插入、删除和查找操作。

    集合底层源码分析.doc

    数据结构中有数组和链表来实现对数据的存储,但这两者基本上是两个极端。所有的数据结构都可以用这两个基本结构来构造的 数组:数组存储区间是连续的,占用内存严重,故空间复杂的很大。但数组的二分查找时间复杂度...

    数据储存结构集合

    从数据存储结构中:线性表,数组,链表,hash表讲起,引申到集合底层存储结构.到排序

    HashMap如何添加元素详解

    存储结构在jdk1.7当中是数组加链表的结构,在jdk1.8当中改为了数组加链表加红黑树的结构。 HashMap在多线程的环境下是不安全的,没有进行加锁措施,所以执行效率快。如果我么需要有一个线程安全的HashMap,可以使用...

    Java的HashMap的工作原理是什么

    hashmap是一个key-value键值对的数据结构,从结构上来讲在jdk1.8...HashMap是用hash表来存储的,在hashmap里为解决hash冲突,使用链地址法,简单来说就是数组加链表的形式来解决,当数据被hash后,得到数组下标,把数

    hashset集合及红黑树简单随手记

    在JDK1.8之前,哈希表底层采用数组+链表实现,即使用链表处理冲突,同一hash值的链表都存储在一个链表里。

    Java魔法解密:揭秘HashMap底层机制.pptx.pptx

    HashMap内部采用数组和链表的方式存储数据,每个元素都包含键值对,通过hash函数将键映射到数组的索引位置,实现高效的查找和插入。 HashMap的性能优化策略。 HashMap在性能优化方面采取多种策略,如扩容机制、负载...

    红黑二叉树详细简介以及使用

    以数组为基本形态,数组中的元素先以链表形式储存,当链表的长 度超过8时(包含数组上的那个链表头)就会将链表转换为红黑树,以加快修改和查询效率。当然除了HashMap还 有很多地方都会用到红黑树,理解红黑树的原理...

    判断链表是否为回文链表leetcode-data-structures-hash-tables-starter:第7周第4天的项目启动文件

    比较和对比数组、链表和哈希表的属性和操作 定义哈希冲突以及如何处理它们 确定从哈希表中添加和删除元素的机制和复杂性 使用哈希表通过缓存(记忆和制表)提高时间复杂度 选择适合解决问题的工具的哈希表 第 1 部分...

    HashMap 源码分析

    首先,我们了解一下HashMap的底层结构历史,在JDK1.8之前采用的是数组+链表的数据结构来存储数据,是不是觉得很熟悉,没错这玩意在1.8之前的结构就和HashTable一样都是采用数组+链表,同样也是通过链地址法(这里简称...

    php中hashtable实现示例分享

    hash函数:用的是time33的散列函数,将一个字符串的key转换成一个数字一个C数组:用来储存桶(buckets)的两个双向的链表:第一个双向链表是数组的每个元素(桶bucket)是一个双向链表,这样做是为了解决hash冲突;...

    算法基础-散列表的原理及基础操作

    文章目录前言正文什么是散列表Hash的数据结构存储数据的数组散列函数Hash的负载因子开放寻址法链表法Hash结构的几个操作读操作开放寻址法的读操作链表法的读操作写操作开放寻址法的写操作链表法的写入扩容总结 ...

    数据结构(C语言版)\Java数据结构和算

    7.8 链表排序和索引表排序 7.9 内部排序小结 7.10 外部排序 7.11 参考文献和选读材料 第8章 Hash法 8.1 引言 8.2 静态Hash法 8.3 动态Hash法 8.4 Bloom滤波器 8.5 参考文献和选读材料 第9章 优先队列 9.1...

    数据结构动画演示完整版

    · 数组(静态数组、动态数组)、线性表、链表(单向链表、双向链表、循环链表)、队列、栈、树(二叉树、查找树、平衡树、线索树、线索树、堆)、图等的定义、存储和操作 · Hash(存储地址计算,冲突处理)的具体...

    超实用的面试题整理

    HashMap内部维护了一个存储数据的Entry数组,HashMap采用链表解决冲突,每一个Entry本质上是一个单向链表。当准备添加一个key-value对时,首先通过hash(key)方法计算hash值,然后通过indexFor(hash,length)求该key-...

    字符数组的存储方式 字符串常量池.docx

    字符串在java程序中被大量使用,为了避免每次都创建相同的字符串对象及内存分配,JVM内部对字符串对象的创建做了一定的优化,在Permanent Generation中专门有一块区域用来存储字符串常量池(一组指针指向Heap中的...

    哈希表(链地址法处理冲突)swust oj#1012

    当两个数的余数相同时仅仅凭借余数作为下标来查找就会发生错误即hash冲突,那么链地址法其实就是将余数相同的数用链表储存起来,那么查找时就遍历余数对应的链表即可(类似邻接表) 题目出处 #include #include ...

    新东方一面(2020春招—Java后端)

    数组和链表的存储方式有什么区别? 集合有哪些? ArrayList和LinkedList的区别? ArrayList的扩容机制? HashMap的原理?扩容机制? 解决Hash冲突的方法?除了开放寻址法和链表法还有吗? 散列函数有哪些?有什么优...

Global site tag (gtag.js) - Google Analytics