散列表(Hash Map)

今天第一次做Leetcode用到了散列表,之前学的数据结构的内容都忘了,正好趁热打铁补一补。

摘自其他博客的一个整合、

一、哈希表简介

数据结构的物理存储结构只有两种:顺序存储结构链式存储结构(像栈,队列,树,图等是从逻辑结构去抽象的,映射到内存中,也这两种物理组织形式),在数组中根据下标查找某个元素,一次定位就可以达到,哈希表利用了这种特性,哈希表的主干就是数组

比如我们要新增或查找某个元素,我们通过把当前元素的关键字 通过某个函数映射到数组中的某个位置,通过数组下标一次定位就可完成操作。

存储位置 = f(关键字)

另一种解释如下:

哈希表就是一种以 键-值(key-indexed) 存储数据的结构,我们只要输入待查找的值即key,即可查找到其对应的值。

哈希的思路很简单,如果所有的键都是整数,那么就可以使用一个简单的无序数组来实现:将键作为索引,值即为其对应的值,这样就可以快速访问任意键的值。这是对于简单的键的情况,我们将其扩展到可以处理更加复杂的类型的键。

使用哈希查找有两个步骤:

  1. 使用哈希函数将被查找的键转换为数组的索引。在理想的情况下,不同的键会被转换为不同的索引值,但是在有些情况下我们需要处理多个键被哈希到同一个索引值的情况。所以哈希查找的第二个步骤就是处理冲突
  2. 处理哈希碰撞冲突。有很多处理哈希碰撞冲突的方法,本文后面会介绍拉链法和线性探测法。

二、JAVA中 HashMap类的方法介绍

此处默认HashMap<Key Value>

1、clear()

clear() 的作用是清空HashMap。它是通过将所有的元素设为null来实现的。

public void clear() {
    modCount++;
    Entry[] tab = table;
    for (int i = 0; i < tab.length; i++)
        tab[i] = null;
    size = 0;
}

2、containsKey()

containsKey() 的作用是判断HashMap是否包含key

public boolean containsKey(Object key) {
    return getEntry(key) != null;
}

containsKey() 首先通过getEntry(key)获取key对应的Entry,然后判断该Entry是否为null

3、containsValue()

containsValue() 的作用是判断HashMap是否包含“值为value”的元素

public boolean containsValue(Object value) {
    // 若“value为null”,则调用containsNullValue()查找
    if (value == null)
        return containsNullValue();

    // 若“value不为null”,则查找HashMap中是否有值为value的节点。
    Entry[] tab = table;
    for (int i = 0; i < tab.length ; i++)
        for (Entry e = tab[i] ; e != null ; e = e.next)
            if (value.equals(e.value))
                return true;
    return false;
}

从中,我们可以看出containsNullValue()分为两步进行处理:第一,若“value为null”,则调用containsNullValue()。第二,若“value不为null”,则查找HashMap中是否有值为value的节点。

containsNullValue() 的作用判断HashMap中是否包含“值为null”的元素

4、entrySet()、values()、keySet()

它们3个的原理类似,这里以entrySet()为例来说明。
entrySet()的作用是返回“HashMap中所有Entry的集合”,它是一个集合。

// 返回“HashMap的Entry集合”
public Set<Map.Entry<K,V>> entrySet() {
    return entrySet0();
}

// 返回“HashMap的Entry集合”,它实际是返回一个EntrySet对象
private Set<Map.Entry<K,V>> entrySet0() {
    Set<Map.Entry<K,V>> es = entrySet;
    return es != null ? es : (entrySet = new EntrySet());
}

// EntrySet对应的集合
// EntrySet继承于AbstractSet,说明该集合中没有重复的EntrySet。
private final class EntrySet extends AbstractSet<Map.Entry<K,V>> {
    public Iterator<Map.Entry<K,V>> iterator() {
        return newEntryIterator();
    }
    public boolean contains(Object o) {
        if (!(o instanceof Map.Entry))
            return false;
        Map.Entry<K,V> e = (Map.Entry<K,V>) o;
        Entry<K,V> candidate = getEntry(e.getKey());
        return candidate != null && candidate.equals(e);
    }
    public boolean remove(Object o) {
        return removeMapping(o) != null;
    }
    public int size() {
        return size;
    }
    public void clear() {
        HashMap.this.clear();
    }
}

5、get()

get() 的作用是获取key对应的value,它的实现代码如下:

public V get(Object key) {
    if (key == null)
        return getForNullKey();
    // 获取key的hash值
    int hash = hash(key.hashCode());
    // 在“该hash值对应的链表”上查找“键值等于key”的元素
    for (Entry<K,V> e = table[indexFor(hash, table.length)];
         e != null;
         e = e.next) {
        Object k;
        if (e.hash == hash && ((k = e.key) == key || key.equals(k)))
            return e.value;
    }
    return null;
}

6、put()

put() 的作用是对外提供接口,让HashMap对象可以通过put()将“key-value”添加到HashMap中

public V put(K key, V value) {
    // 若“key为null”,则将该键值对添加到table[0]中。
    if (key == null)
        return putForNullKey(value);
    // 若“key不为null”,则计算该key的哈希值,然后将其添加到该哈希值对应的链表中。
    int hash = hash(key.hashCode());
    int i = indexFor(hash, table.length);
    for (Entry<K,V> e = table[i]; e != null; e = e.next) {
        Object k;
        // 若“该key”对应的键值对已经存在,则用新的value取代旧的value。然后退出!
        if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {
            V oldValue = e.value;
            e.value = value;
            e.recordAccess(this);
            return oldValue;
        }
    }

    // 若“该key”对应的键值对不存在,则将“key-value”添加到table中
    modCount++;
    addEntry(hash, key, value, i);
    return null;
}

原文地址:https://www.cnblogs.com/Thermal/p/9603532.html

时间: 2024-08-03 17:54:13

散列表(Hash Map)的相关文章

Java 散列表 hash table

Java 散列表 hash table @author ixenos hash table, HashTable, HashMap, HashSet hash table 是一种数据结构 hash table 为每个对象计算一个整数,该整数被称为散列码 hash code hash code 是由对象的实例域产生的一个整数,具有不同的数据域的对象将产生不同的hash code 如果自定义类,就要负责实现这个类的hashCode方法,注意要与equals方法兼容,即如果a.equals(b)为tr

散列表(Hash table)及其构造

散列表(Hash table) 散列表,是根据关键码值(Key value)而直接进行访问的数据结构.它通过把关键码值映射到表中一个位置来访问记录,以加快查找的速度.这个映射函数叫做散列函数,存放记录的数组叫做散列表. 已知的查找方法: 1.顺序查找 O(N) 2.二分查找(静态查找) O(log2N) 3.二叉搜索树 O(h) h为二叉树的高度 平衡二叉树 O(log2N) Q:如何快速搜索到需要的关键字?如果关键字不方便比较怎么办? 查找的本质:已知对象找位置 有序安排对象:全序.半序 直接

算法导论-散列表(Hash Table)

目录 引言 直接寻址 散列寻址 散列函数 除法散列 乘法散列 全域散列 完全散列 碰撞处理方法 链表法 开放寻址法 线性探查 二次探查 双重散列 随机散列 再散列问题 完整源码(C++) 参考资料 内容 1.引言 如果想在一个n个元素的列表中,查询元素x是否存在于列表中,首先想到的就是从头到尾遍历一遍列表,逐个进行比较,这种方法效率是Θ(n):当然,如果列表是已经排好序的话,可以采用二分查找算法进行查找,这时效率提升到Θ(logn);  本文中,我们介绍散列表(HashTable),能使查找效率

散列表(hash table)&mdash;&mdash;算法导论(13)

1. 引言     许多应用都需要动态集合结构,它至少需要支持Insert,search和delete字典操作.散列表(hash table)是实现字典操作的一种有效的数据结构. 2. 直接寻址表     在介绍散列表之前,我们前介绍直接寻址表.     当关键字的全域U(关键字的范围)比较小时,直接寻址是一种简单而有效的技术.我们假设某应用要用到一个动态集合,其中每个元素的关键字都是取自于全域U={0,1,-,m-1},其中m不是一个很大的数.另外,假设每个元素的关键字都不同.    为表示动

散列表(hash表)

1. hash表: 又称散列表,以key-value的形式存储数据,能够由key快速定位到其指定的value,而不经过查找.它采用了函数式的映射思想,将记录的存储位置与关键词相关联,从而快速定位进行查找,复杂度为O(1). 2. hash函数: key和value的映射关系称为HASH函数,通过该函数可以计算key所对应的存储位置(表中存储位置,不是实际物理地址),即HASH地址. 构造HASH地址的方法有: (1)直接定址法:取关键词或关键词的某个线性函数为hash地址. (2)平方取中法:关

算法导论11.2散列表Hash tables链式法解决碰撞

/* * IA_11.2ChainedHash.cpp * * Created on: Feb 12, 2015 * Author: sunyj */ #include <stdint.h> #include <iostream> #include <string.h> // CHAINED-HASH-INSERT(T, x) // insert x at the head of list T[h(x.key)] // CHAINED-HASH-SEARCH(T, k)

linux内核中的哈希散列表

    介绍一下linux内核中的哈希散列表的实现,在linux内核中哈希散列表(hash list)用的非常的多, 并且自己以后在做软件设计的时候,也非常有可能用到.毕竟,哈希散列表在数据的查找过程中非常的方便.      linux内核对哈希散列表的实现非常的完美,所以非常有必要学习一下. 在哈希散列表的实现过程中,用到的两个非常有用的结构体:      哈希散列表头结构体 :                          struct hlist_head               

数据结构之散列表总结

散列表的概念 注意:    ①由同一个散列函数.不同的解决冲突方法构造的散列表,其平均查找长度是不相同的.     ②散列表的平均查找长度不是结点个数n的函数,而是装填因子α(填入表中的记录个数/散列表的槽数    n/m).因此在设计散列表时可选择α以控制散列表的平均查找长度.(平均查找长度=总查找(插入)/记录个数)          通过链接法解决冲突:成功查找的期望查找长度O(1+a), 不成功查找的平均查找长度也为O(1+a).         开放寻址解决冲突:引入探查序列,对于a<

算法导论之十(十一章散列表11.1-4大数组实现直接寻址方式的字典操作)

11.1-4题目: 我们希望在一个非常大的数组上,通过利用直接寻址的方式来实现一个字典.开始时,该数组中可能包含一些无用信息,但要对整个数组进行初始化是不太实际的,因为该数组的规模太大.请给出在大数组上实现直接寻址字典的方式.每个存储对象占用O(1)空间:SEARCH.INSEART.DELETE操作的时间均为O(1):并且对数据结构初始化的时间为O(1).(提示:可以利用一个附加数组,处理方式类似于栈,其大小等于实际存储在字典中的关键字数目,以帮助确定大数组中某个给定的项是否有效). 想法:

算法——散列表

散列表 算法——散列表 散列表(hash table):键值(key_value)映射,Python提供的哈希列表实现为字典. 作用: 模拟映射关系 便于查找 避免重复 缓存/记住数据,以免服务器再通过处理来生成它们 # hash_table.py 哈希表 # 避免重复 def vote(li): voters = {} for i in li: if i not in voters: voters[i] = True else: print(i + ' has already voted.')