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

深圳做网站建设和维护专员管理层当当网网站建设建议

深圳做网站建设和维护专员管理层,当当网网站建设建议,7k7k小游戏大全网页版,用一个织梦程序做两个网站上一节提到#xff0c;通常情况下哈希函数的输入空间远大于输出空间#xff0c;因此理论上哈希冲突是不可避免的。比如#xff0c;输入空间为全体整数#xff0c;输出空间为数组容量大小#xff0c;则必然有多个整数映射至同一桶索引。 哈希冲突会导致查询结果错误#…上一节提到通常情况下哈希函数的输入空间远大于输出空间因此理论上哈希冲突是不可避免的。比如输入空间为全体整数输出空间为数组容量大小则必然有多个整数映射至同一桶索引。 哈希冲突会导致查询结果错误严重影响哈希表的可用性。为解决该问题我们可以每当遇到哈希冲突就进行哈希表扩容直至冲突消失为止。此方法简单粗暴且有效但效率太低因为哈希表扩容需要进行大量的数据搬运与哈希值计算。为了提升效率我们可以采用以下策略。 改良哈希表数据结构使得哈希表可以在出现哈希冲突时正常工作。仅在必要时即当哈希冲突比较严重时才执行扩容操作。 哈希表的结构改良方法主要包括“链式地址”和“开放寻址”。 11.1 链式地址 在原始哈希表中每个桶仅能存储一个键值对。链式地址(separate chaining)将单个元素转换为链表将键值对作为链表节点将所有发生冲突的键值对都存储在同一链表中。下图展示了一个链式地址哈希表的例子。 基于链式地址实现的哈希表的操作方法发生了以下变化。 查询元素输入 key 经过哈希函数得到桶索引即可访问链表头节点然后遍历链表并对比 key 以查找目标键值对。添加元素首先通过哈希函数访问链表头节点然后将节点键值对添加到链表中。删除元素根据哈希函数的结果访问链表头部接着遍历链表以查找目标节点并将其删除。 链式地址存在以下局限性。 占用空间增大链表包含节点指针它相比数组更加耗费内存空间。查询效率降低因为需要线性遍历链表来查找对应元素。 以下代码给出了链式地址哈希表的简单实现需要注意两点。 使用列表动态数组代替链表从而简化代码。在这种设定下哈希表数组包含多个桶每个桶都是一个列表。以下实现包含哈希表扩容方法。当负载因子超过 2/3 时我们将哈希表扩容至原先的 2 倍。 /* 链式地址哈希表 */ class HashMapChaining {private:int size; // 键值对数量int capacity; // 哈希表容量double loadThres; // 触发扩容的负载因子阈值int extendRatio; // 扩容倍数vectorvectorPair * buckets; // 桶数组public:/* 构造方法 */HashMapChaining() : size(0), capacity(4), loadThres(2.0 / 3.0), extendRatio(2) {buckets.resize(capacity);}/* 析构方法 */~HashMapChaining() {for (auto bucket : buckets) {for (Pair *pair : bucket) {// 释放内存delete pair;}}}/* 哈希函数 */int hashFunc(int key) {return key % capacity;}/* 负载因子 */double loadFactor() {return (double)size / (double)capacity;}/* 查询操作 */string get(int key) {int index hashFunc(key);// 遍历桶若找到 key 则返回对应 valfor (Pair *pair : buckets[index]) {if (pair-key key) {return pair-val;}}// 若未找到 key 则返回空字符串return ;}/* 添加操作 */void put(int key, string val) {// 当负载因子超过阈值时执行扩容if (loadFactor() loadThres) {extend();}int index hashFunc(key);// 遍历桶若遇到指定 key 则更新对应 val 并返回for (Pair *pair : buckets[index]) {if (pair-key key) {pair-val val;return;}}// 若无该 key 则将键值对添加至尾部buckets[index].push_back(new Pair(key, val));size;}/* 删除操作 */void remove(int key) {int index hashFunc(key);auto bucket buckets[index];// 遍历桶从中删除键值对for (int i 0; i bucket.size(); i) {if (bucket[i]-key key) {Pair *tmp bucket[i];bucket.erase(bucket.begin() i); // 从中删除键值对delete tmp; // 释放内存size--;return;}}}/* 扩容哈希表 */void extend() {// 暂存原哈希表vectorvectorPair * bucketsTmp buckets;// 初始化扩容后的新哈希表capacity * extendRatio;buckets.clear();buckets.resize(capacity);size 0;// 将键值对从原哈希表搬运至新哈希表for (auto bucket : bucketsTmp) {for (Pair *pair : bucket) {put(pair-key, pair-val);// 释放内存delete pair;}}}/* 打印哈希表 */void print() {for (auto bucket : buckets) {cout [;for (Pair *pair : bucket) {cout pair-key - pair-val , ;}cout ]\n;}} };值得注意的是当链表很长时查询效率 O(n) 很差。此时可以将链表转换为“AVL 树”或“红黑树”从而将查询操作的时间复杂度优化至 O(logn) 。 11.2 开放寻址  开放寻址(open addressing)不引入额外的数据结构而是通过“多次探测”来处理哈希冲突探测方式主要包括线性探测、平方探测、多次哈希等。 下面以线性探测为例介绍开放寻址哈希表的工作机制。 11.2.1 线性探测 线性探测采用固定步长的线性搜索来进行探测其操作方法与普通哈希表有所不同。 插入元素通过哈希函数计算桶索引若发现桶内已有元素则从冲突位置向后线性遍历步长通常为 1 直至找到空桶将元素插入其中。查找元素若发现哈希冲突则使用相同步长向后线性遍历直到找到对应元素返回 value 即可如果遇到空桶说明目标元素不在哈希表中返回 None 。 下图展示了开放寻址线性探测哈希表的键值对分布。根据此哈希函数最后两位相同的 key 都会被映射到相同的桶。而通过线性探测它们被依次存储在该桶以及之下的桶中。 然而线性探测容易产生“聚集现象”。具体来说数组中连续被占用的位置越长这些连续位置发生哈希冲突的可能性越大从而进一步促使该位置的聚堆生长形成恶性循环最终导致增删查改操作效率劣化。 值得注意的是我们不能在开放寻址哈希表中直接删除元素。这是因为删除元素会在数组内产生一个空桶 None 而当查询元素时线性探测到该空桶就会返回因此在该空桶之下的元素都无法再被访问到程序可能误判这些元素不存在。 为了解决该问题我们可以采用懒删除(lazy deletion)机制它不直接从哈希表中移除元素而是利用一个常量 TOMBSTONE 来标记这个桶。在该机制下None 和 TOMBSTONE 都代表空桶都可以放置键值对。但不同的是线性探测到 TOMBSTONE 时应该继续遍历因为其之下可能还存在键值对。 然而懒删除可能会加速哈希表的性能退化。这是因为每次删除操作都会产生一个删除标记随着 TOMBSTONE 的增加搜索时间也会增加因为线性探测可能需要跳过多个 TOMBSTONE 才能找到目标元素。 为此考虑在线性探测中记录遇到的首个 TOMBSTONE 的索引并将搜索到的目标元素与该 TOMBSTONE 交换位置。这样做的好处是当每次查询或添加元素时元素会被移动至距离理想位置探测起始点更近的桶从而优化查询效率。 以下代码实现了一个包含懒删除的开放寻址线性探测哈希表。为了更加充分地使用哈希表的空间我们将哈希表看作一个“环形数组”当越过数组尾部时回到头部继续遍历。 /* 开放寻址哈希表 */ class HashMapOpenAddressing {private:int size; // 键值对数量int capacity 4; // 哈希表容量const double loadThres 2.0 / 3.0; // 触发扩容的负载因子阈值const int extendRatio 2; // 扩容倍数vectorPair * buckets; // 桶数组Pair *TOMBSTONE new Pair(-1, -1); // 删除标记public:/* 构造方法 */HashMapOpenAddressing() : size(0), buckets(capacity, nullptr) {}/* 析构方法 */~HashMapOpenAddressing() {for (Pair *pair : buckets) {if (pair ! nullptr pair ! TOMBSTONE) {delete pair;}}delete TOMBSTONE;}/* 哈希函数 */int hashFunc(int key) {return key % capacity;}/* 负载因子 */double loadFactor() {return (double)size / capacity;}/* 搜索 key 对应的桶索引 */int findBucket(int key) {int index hashFunc(key);int firstTombstone -1;// 线性探测当遇到空桶时跳出while (buckets[index] ! nullptr) {// 若遇到 key 返回对应桶索引if (buckets[index]-key key) {// 若之前遇到了删除标记则将键值对移动至该索引if (firstTombstone ! -1) {buckets[firstTombstone] buckets[index];buckets[index] TOMBSTONE;return firstTombstone; // 返回移动后的桶索引}return index; // 返回桶索引}// 记录遇到的首个删除标记if (firstTombstone -1 buckets[index] TOMBSTONE) {firstTombstone index;}// 计算桶索引越过尾部返回头部index (index 1) % capacity;}// 若 key 不存在则返回添加点的索引return firstTombstone -1 ? index : firstTombstone;}/* 查询操作 */string get(int key) {// 搜索 key 对应的桶索引int index findBucket(key);// 若找到键值对则返回对应 valif (buckets[index] ! nullptr buckets[index] ! TOMBSTONE) {return buckets[index]-val;}// 若键值对不存在则返回空字符串return ;}/* 添加操作 */void put(int key, string val) {// 当负载因子超过阈值时执行扩容if (loadFactor() loadThres) {extend();}// 搜索 key 对应的桶索引int index findBucket(key);// 若找到键值对则覆盖 val 并返回if (buckets[index] ! nullptr buckets[index] ! TOMBSTONE) {buckets[index]-val val;return;}// 若键值对不存在则添加该键值对buckets[index] new Pair(key, val);size;}/* 删除操作 */void remove(int key) {// 搜索 key 对应的桶索引int index findBucket(key);// 若找到键值对则用删除标记覆盖它if (buckets[index] ! nullptr buckets[index] ! TOMBSTONE) {delete buckets[index];buckets[index] TOMBSTONE;size--;}}/* 扩容哈希表 */void extend() {// 暂存原哈希表vectorPair * bucketsTmp buckets;// 初始化扩容后的新哈希表capacity * extendRatio;buckets vectorPair *(capacity, nullptr);size 0;// 将键值对从原哈希表搬运至新哈希表for (Pair *pair : bucketsTmp) {if (pair ! nullptr pair ! TOMBSTONE) {put(pair-key, pair-val);delete pair;}}}/* 打印哈希表 */void print() {for (Pair *pair : buckets) {if (pair nullptr) {cout nullptr endl;} else if (pair TOMBSTONE) {cout TOMBSTONE endl;} else {cout pair-key - pair-val endl;}}} };11.2.2 平方探测 平方探测与线性探测类似都是开放寻址的常见策略之一。当发生冲突时平方探测不是简单地跳过一个固定的步数而是跳过“探测次数的平方”的步数即 1,4,9,… 步。 平方探测主要具有以下优势。 平方探测通过跳过平方的距离试图缓解线性探测的聚集效应。平方探测会跳过更大的距离来寻找空位置有助于数据分布得更加均匀。 然而平方探测也并不是完美的。 仍然存在聚集现象即某些位置比其他位置更容易被占用。由于平方的增长平方探测可能不会探测整个哈希表这意味着即使哈希表中有空桶平方探测也可能无法访问到它。 11.2.3 多次哈希 顾名思义多次哈希方法使用多个哈希函数 f1(x)、f2(x)、f3(x)、… 进行探测。 插入元素若哈希函数 f1(x) 出现冲突则尝试 f2(x) 以此类推直到找到空桶后插入元素。查找元素在相同的哈希函数顺序下进行查找直到找到目标元素时返回若遇到空桶或已尝试所有哈希函数说明哈希表中不存在该元素则返回 None 。 与线性探测相比多次哈希方法不易产生聚集但多个哈希函数会带来额外的计算量。 11.3 编程语言的选择  各个编程语言采取了不同的哈希表实现策略以下举几个例子。 Python 采用开放寻址。字典 dict 使用伪随机数进行探测。Java 采用链式地址。自 JDK 1.8 以来当 HashMap 内数组长度达到 64 且链表长度达到 8 时链表会转换为红黑树以提升查找性能。Go 采用链式地址。Go 规定每个桶最多存储 8 个键值对超出容量则连接一个溢出桶。当溢出桶过多时会执行一次特殊的等量扩容操作以确保性能。
http://www.dnsts.com.cn/news/97831.html

相关文章:

  • 建设网上商城网站的目的和意义门户网站建设收费
  • 龙口网站制作开发菏泽网站建设
  • 建网站方案红酒商城网站建设方案
  • ppt模板免费下载哪个网站好dw学生个人网页制作视频
  • 请详细说明网站开发流程及原则励志网站源码
  • 公司网站建设知乎那个网站是专门做渔具的
  • 南昌寻南昌网站设计wordpress 用户前端
  • 网站制作 商城佛山做网站的
  • 网络营销主要是什么免费seo教程
  • 一个完整的网站怎么做html网页模板网站
  • app设计网站推荐亚马逊雨林有原始部落吗
  • 杭州企业网站设计wordpress 七牛 水印
  • 西安建设网站的公司简介百度推广怎么才能效果好
  • 外贸跨境电商网站建设开发和幼儿做网站爱
  • 搭建网站需要程序织梦网站模板源码下载
  • 郑州建网站的好处长春旅游网站开发
  • 组装电脑报价网站源码黄骅贴吧桃花路
  • 广州金山大厦 网站建设济南网站建设公司电子商务网站
  • wordpress站内信插件网站视频无法播放怎么办
  • 河南怎么样做网站外贸网有哪些
  • 杭州商城网站开发免费网页空间
  • 深圳专业做网站哪家专业做网站内存最小源码
  • 微信官方网站首页郑州网站建设伟置
  • 全flash网站下载seo搜索引擎优化ppt
  • html5网站动效怎么做台州网站公司建站
  • 医美的网站主页怎么做东平可信的网站建设
  • 网站制作需要什么资料线上宣传有哪些好的方式方法
  • 网站数据抓取怎么做怎么查看网站是否被百度收录
  • 太姥山镇建设的网站wordpress登录密码重置
  • 昆明市做网站海口seo关键词优化