腾讯云 wordpress建站网页游戏入口

当前位置: 首页 > news >正文

腾讯云 wordpress建站,网页游戏入口,深圳网站制作首选灵点网络,wordpress 后台乱码目录 哈希表的基本原理 哈希表的优点 哈希表的缺点 应用场景 闭散列法 开散列法 开放定值法Open Addressing——线性探测的模拟实现 超大重点部分评析 链地址法Separate Chaining——哈希桶的模拟实现 哈希表#xff08;Hash Table#xff09;是一种数据结构#x…目录 哈希表的基本原理 哈希表的优点 哈希表的缺点 应用场景 闭散列法 开散列法 开放定值法Open Addressing——线性探测的模拟实现 超大重点部分评析 链地址法Separate Chaining——哈希桶的模拟实现 哈希表Hash Table是一种数据结构它通过将键Key映射到值Value的方式来实现快速的数据存储与查找。哈希表的核心概念是哈希函数该函数用于将输入的键转换为哈希值通常是一个整数并根据这个哈希值将数据存储在数组中的特定位置。 哈希表的基本原理 哈希函数这是哈希表的关键部分它接收一个键并返回一个整数哈希值。理想情况下哈希函数会将不同的键均匀地分布到数组的不同位置上。 数组桶Bucket哈希表通常是基于数组实现的哈希函数的输出决定了键值对应该存储在数组中的哪个位置。 处理冲突由于不同的键可能会生成相同的哈希值称为哈希冲突需要有机制来处理这些冲突。常见的处理方式包括 链地址法Separate Chaining每个数组位置存储一个链表所有哈希值相同的键值对都存储在同一个链表中。开放地址法Open Addressing当冲突发生时查找数组中的下一个空闲位置存储键值对常见的方式包括线性探测、二次探测和双重散列。 查找和插入哈希表允许以 O(1) 的时间复杂度进行查找和插入操作在理想情况下即只需一次哈希函数计算和一次数组访问即可找到或插入数据。
哈希表的优点 快速的查找、插入和删除在平均情况下哈希表的查找、插入和删除操作都是常数时间O(1)这使其非常高效。简单实现哈希表相对容易实现且不需要复杂的数据结构操作。 哈希表的缺点 空间浪费如果哈希表的负载因子存储的元素数量与数组大小的比值较高容易导致冲突增加从而降低性能。为避免冲突通常会使用更大的数组这会导致空间浪费。无法有序遍历哈希表中的数据是无序的如果需要顺序访问数据需要额外的处理。哈希函数质量哈希表的性能高度依赖于哈希函数的质量差劲的哈希函数可能导致较多的冲突。 应用场景 哈希表在许多场景中广泛使用包括但不限于 字典实现例如Python 中的字典dict就是通过哈希表实现的。缓存哈希表常用于实现缓存机制如 LRU 缓存。集合操作例如在查找某个元素是否在集合中时哈希表可以显著提高效率。位图略布隆过滤器略 通过哈希表可以在大型数据集上快速执行查找、插入和删除操作这使得它在实际应用中非常实用。 所以依照哈希表的基本原理可以看出哈希表的底层结构可以分为以下两种这两种方法的本质都是除留余数法 闭散列法 闭散列法也叫开放定址法当发生哈希冲突时如果哈希表未被装满说明在哈希表中必然还有 空位置那么可以把key存放到冲突位置中的“下一个” 空位置中去。那如何寻找下一个空位置 呢 比如2.1中的场景现在需要插入元素44先通过哈希函数计算哈希地址hashAddr为4 因此44理论上应该插在该位置但是该位置已经放了值为4的元素即发生哈希冲突。从发生冲突的位置开始依次向后探测直到寻找到下一个空位置为止。 插入 通过哈希函数获取待插入元素在哈希表中的位置 如果该位置中没有元素则直接插入新元素如果该位置中有元素发生哈希冲突 使用线性探测找到下一个空位置插入新元素。 删除采用闭散列处理哈希冲突时不能随便物理删除哈希表中已有的元素若直接删除元素 会影响其他元素的搜索。比如删除元素4如果直接删除掉44查找起来可能会受影 响。因此线性探测采用标记的伪删除法来删除一个元素。 上面有一个名词为线性探测就是当待插入元素出现哈希冲突时一个一个往下寻找空位置称为线性探测每次n的固定次方倍往下找称为二次探测两种探测的本质解决的问题完全一致所有我们就只使用线性探测进行实现。 开散列法 开散列法又叫链地址法(开链法)首先对关键码集合用散列函数计算散列地址具有相同地 址的关键码归于同一子集合每一个子集合称为一个桶各个桶中的元素通过一个单链表链 接起来各链表的头结点存储在哈希表中。代码总体实现的功能和上面的闭散列法差不多。 从上图可以看出开散列中每个桶中放的都是发生哈希冲突的元素。 开放定值法Open Addressing——线性探测的模拟实现 #includeiostream #includevector using namespace std;//namespace hashbucket//包一个命名空间就可以调用了 //{    templateclass K     struct Hashfunc     {         size_t operator()(const K key)         {             return (size_t)key;//由于像当K为string时不可以直接强制转换成整型所以如果哈希表需要插入string类型的数据就需要写string的特化版本如下        }     };     //struct StringHashFunc     //{     //    size_t operator()(const string s)     //    {     //        size_t hash 0;     //        for (auto e : s)     //        {     //            hash * 31;     //            hash e;     //        }     //     //        return hash;     //    }     //};    template     struct Hashfuncstring//可以直接在这里写模板的特化前提是前面有写过了且名称一样遇到和特化吻合的会优先匹配特化版本    {         size_t operator()(const string key)         {             size_t hash 0;             for (auto e : key)             {                 hash e;                 hash * 31;//根据某篇文章可得多乘31可以有效避免很多重复            }             return hash;         }     };     enum status     {         EXIST,         EMPTY,         DELETE//三个状态这里定义了哈希表中的数据的多个状态在红黑树的模拟实现中有用过这样方便判断此位置的数据状态方便以后的线性探测和删除数据等    }; templateclass K, class V     struct Hashdata     {         pairK, V _kv;         status s EMPTY;//刚开始时所有空位的状态都是EMPTY    }; templateclass K, class V, class Hash HashfuncK     class Hashtable     {     public:         Hashtable()         {             _table.resize(10);//直接在初始化时开10个空间        }         Hash ha;//仿函数        bool insert(const pairK, V kv)         {             if (Find(kv.first))             {                 return false;             }             if (10 * n / _table.size() 7)             {                 //vectorHashDataK, V newTables(_tables.size() * 2);                  遍历旧表 将所有数据映射到新表                  …                 //_tables.swap(newTables);                 //以上方法无法复用insert                HashtableK, V, Hash newht;//是KV                newht._table.resize(2 * _table.size());//_table肯定是类里的不然要指定的                for (int i 0; i _table.size(); i)                 {                     newht.insert(_table[i]._kv);//复用                }                 _table.swap(newht._table);//交换            }             size_t hashi ha(kv.first) % _table.size();//非整型不能取%所以使用仿函树转换一下            while (_table[hashi].s EXIST)             {                 hashi;                 hashi % _table.size();//hashi不断的的时候有可能会超出整个vector的数据的长度所以需要再%_table.size()使其处在最后时下一步回到数据的开头所以可以看出hashi是不断循环的。            }             _table[hashi]._kv kv;             _table[hashi].s EXIST;             n;//每成功插入一个数据其负载因子就加1            return true;         }         vectorHashdataK, V* begin() const         {             return _table[0];         } vectorHashdataK, V* end() const         {             return _table[_table.size()];         }         HashdataK, V* Find(const K key)         {             size_t hashi ha(key) % _table.size();             size_t n hashi;             while (_table[hashi].s ! DELETE)             {                 if (_table[hashi]._kv.first key _table[hashi].s EXIST)//由于下面的删除逻辑为不删除数据只改名数据的状态所以当找到相等时这个数据有可能会有两种状态EXIST和DELETE                {                     return _table[hashi];                 }                 hashi;                 hashi hashi % _table.size();                 if (hashi n)                 {                     return nullptr;                 }             }             return nullptr;         } bool Erase(const K key)//只要传一个K就可以了        {             HashdataK, V* ret Find(key);             if (ret)             {                 ret-s DELETE;                 return true;             }             return false;         }     private:         vectorHashdataK, V _table;         size_t n 0;//表中存储数据个数, n为负载因子    }; //} 超大重点部分评析 这边说一下扩容的逻辑由于插入逻辑里面hashi是不断循环的所以当哈希表里面数据都占满时hashi会陷入死循环但是就算没有全部占满如果已占数据过多就会使得下一个数据在插入时哈希冲突过多使时间复杂度变大所以为了避免这种情况引入负载因子当插入数据占比百分之70时也就是负载因子/空间数据容纳最大个数 0.7时将容器的容量扩大成原来的双倍。 再观察上面的扩容代码为什么不能直接再原空间之间扩容呢需要另开一个空间再交换回去因为如果之间在原空间扩容会使得size(数据最大个数)变大使得原本的数据的映射乱了。像现在这种扩容写法交换swap之后由于新创建的临时变量newht出函数体就会销毁了也就会顺带释放掉swap交给它的原空间这样扩容之后的空间就保留下来了所以不需要考虑旧表没有被释放的问题。 最后注意一下这个Erase的写法很多人可能会认为删除某个元素需要像之前顺序表的那种写法(毕竟每个数据就在顺序表里面)删除了这个数据让这个数据后面的数据依次往前移时间复杂度就是O(n)这里不是不可以但是如果你这么写就麻烦了因为每个数据都外带了一个数据状态按顺序表那么写的话就忽略的状态这个东西。 链地址法Separate Chaining——哈希桶的模拟实现 预知后事如何请持续关注本系列内容