• Java数据结构——哈希表


    什么是哈希表?
    哈希表是一种根据关键码去寻找值的数据映射结构,该结构通过把关键码映射的位置去寻找存放值的地方。

    哈希表充分体现了算法设计领域的经典思想:空间换时间。哈希表是时间和空间之间的平衡。其中的哈希函数是最重要的,“键”通过哈希函数得到的“索引”分布越均匀越好。但是哈希表会失去顺序性。

    哈希函数的设计
    对于整型

    1. 小范围正整数直接使用
    2. 小范围负整数进行偏移 -100~100 ----> 0~200
    3. 大整数:模一个素数

    对于浮点型
    转成整型处理


    对于字符串
    也是转成整型处理

    int hash=0forint i=0;i<s.length();i++
    {
    hash=(hash*B+s.charAt(i))%M;
    }

    对于复合类型
    依然转成整型处理

    hash(code)=((((c%M)*B+o)%M*B+d)%M*B+e)%M; 

    但是,转成整型处理,并不是唯一的方法

    哈希函数设计原则

    1. 一致性:如果a==b,则hash(a)==hash(b),反之不一定成立
    2. 高效性:计算高效简便
    3. 均匀性:哈希值分布均匀

    哈希冲突的处理
    链地址法(O(1))

    在jdk7之前是数组+链表
    jdk8开始,当链表长度超过一定值后会转换成红黑树

    开放地址法
    不会形成链,当遇到哈希冲突时,直接往冲突的位置的后面的第一个空的位置里放。当这个哈希表足 够大且元素存的足够多的时候,会导致查找下一个空位的效率变得很低。

    实现一个自己的哈希表

    import java.util.TreeMap;
    
    public class HashTable<K, V> {
    private TreeMap<K, V>[] hashtable;
    private int M;
    private static final int upperTol = 10;
    private static final int lowerTol = 2;
    private static final int initCapacity = 7;
    private final int[] capacity = { 53, 97, 193, 389, 769, 1543, 3079, 6151, 12289, 24593, 49157, 98317, 196613,
    393241, 786433, 1572869, 3145739, 6291469, 12582917, 25165843, 50331653, 100663319, 201326611, 402653189,
    805306457, 1610612741 };
    private int capacityIndex = 0;
    private int size;
    
    public HashTable() {
    this.M = capacity[capacityIndex];
    size = 0;
    hashtable = new TreeMap[M];
    for (int i = 0; i < M; i++) {
    hashtable[i] = new TreeMap<>();
    }
    }
    
    private int hash(K Key) {
    return (Key.hashCode() & 0x7fffffff) % M;
    }
    
    public int getSize() {
    return size;
    }
    
    public void add(K key, V value) {
    TreeMap<K, V> map = hashtable[hash(key)];
    if (map.containsKey(key)) {
    map.put(key, value);
    } else {
    map.put(key, value);
    size++;
    if (size >= upperTol * M && capacityIndex + 1 < capacity.length) {
    capacityIndex++;
    resize(capacity[capacityIndex]);
    }
    }
    }
    
    public V remove(K key) {
    TreeMap<K, V> map = hashtable[hash(key)];
    V ret = null;
    if (map.containsKey(key)) {
    ret = map.remove(key);
    size--;
    if (size < lowerTol * M && capacityIndex - 1 >= 0) {
    capacityIndex--;
    resize(capacity[capacityIndex]);
    }
    }
    return ret;
    }
    
    public void set(K key, V value) {
    TreeMap<K, V> map = hashtable[hash(key)];
    if (!map.containsKey(key)) {
    throw new IllegalArgumentException("don't find");
    } else {
    map.put(key, value);
    }
    }
    
    public boolean contain(K key) {
    return hashtable[hash(key)].containsKey(key);
    }
    
    public V get(K key) {
    TreeMap<K, V> map = hashtable[hash(key)];
    return map.get(key);
    }
    
    private void resize(int newM) {
    TreeMap<K, V>[] newTable = new TreeMap[newM];
    for (int i = 0; i < newM; i++) {
    newTable[i] = new TreeMap<>();
    }
    int oldM = M;
    this.M = newM;
    for (int i = 0; i < oldM; i++) {
    TreeMap<K, V> map = hashtable[i];
    for (K key : map.keySet()) {
    newTable[hash(key)].put(key, map.get(key));
    }
    }
    this.hashtable = newTable;
    }
    }
  • 相关阅读:
    Android 6.0 运行时权限处理完全解析
    Android 6.0 运行时权限处理
    史上比较用心的纯代码实现 AutoLayout
    hdu 4268 Alice and Bob(multiset|段树)
    在单调的队列
    Python re正则表达式
    minihomepage.exe 百度视频迷你主页
    POJ 2914 Minimum Cut 最小割图论
    移植kl档,但调用默认Generic.kl解决的方法
    iOS 删除黑色边框线导航栏&amp;删除搜索框的阴影边界线和中黑色文本输入框 - 解
  • 原文地址:https://www.cnblogs.com/ericz2j/p/10800519.html
Copyright © 2020-2023  润新知