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

数据结构(c++版):深入理解哈希计数器

哈希计数器:智能投票统计系统的奇妙设计在理解了哈希表这个"智能图书馆"之后,让我们来看看一个更加专业的应用——哈希计数器。这是一个专门用于统计频率的高效工具,就像是一个智能的投票统计系统。

哈希计数器的设计理念

想象你正在组织一场大型选举,有成千上万的候选人(键),你需要实时统计每个候选人的得票数。哈希计数器就是为此而生的专业系统:

template<typename Keytype>
class Hashcount {
private:int countsize;          // 投票箱总数int countindex;          // 当前已分配的投票箱编号int* count;              // 每个投票箱的票数记录Hashtable<Keytype, int>* hash;  // 候选人到投票箱的映射表
};

核心思想​:将哈希表的查找能力与数组的快速访问能力完美结合!

系统初始化:建立投票站

template<typename Keytype>
Hashcount<Keytype>::Hashcount(int size)
{countsize = size;        // 设置投票箱总数countindex = 0;          // 从0号投票箱开始分配count = new int[countsize];  // 准备所有投票箱hash = NULL;             // 初始时没有映射关系reset();                 // 初始化整个系统
}

比喻​:就像建立选举投票站:

  • countsize:准备1000个投票箱
  • countindex:从第一个投票箱开始编号
  • count数组:每个投票箱都有一个计数器记录票数
  • hash:候选人名册,记录每个候选人对应哪个投票箱

重置机制:新一届选举开始

template<typename Keytype>
void Hashcount<Keytype>::reset() {if (hash)  // 如果已有选举数据,先清理{delete hash;hash = NULL;}hash = new Hashtable<Keytype, int>(countsize);  // 新建候选人名册countindex = 0;  // 投票箱编号归零for (int i = 0; i < countsize; i++){count[i] = 0;  // 所有投票箱清零}
}

工作流程​:

  1. 清理历史​:删除上一届选举的所有记录
  2. 新建名册​:准备新的候选人登记表
  3. 重置计数器​:所有投票箱归零,准备重新计票

核心操作:投票统计的智慧

增加票数:为新老候选人投票

template<typename Keytype>
int Hashcount<Keytype>::add(const Keytype& key)
{int idx;  // 投票箱编号if (!hash->find(key, idx))  // 如果是新候选人{idx = countindex++;  // 分配新的投票箱hash->insert(key, idx);  // 登记候选人信息}return ++count[idx];  // 票数增加并返回当前票数
}

生动场景​:

  • 情况1​:老候选人"张三"来投票

    • 系统查名册:张三 → 5号投票箱
    • 直接到5号箱投一票
    • 返回:张三当前得票数
  • 情况2​:新候选人"李四"首次参选

    • 系统查名册:李四未登记
    • 分配新的投票箱(比如15号)
    • 在名册登记:李四 → 15号投票箱
    • 在15号箱投入第一票
    • 返回:李四得票数(1票)

查询票数:实时统计查看

template<typename Keytype>
int Hashcount<Keytype>::get(const Keytype& key)
{int idx;if (hash->find(key, idx))  // 找到候选人的投票箱{return count[idx];  // 返回该投票箱的票数}return 0;  // 未登记的候选人得票为0
}

比喻​:记者查询某个候选人的实时得票

  • 先查名册找到候选人对应的投票箱编号
  • 直接读取该投票箱的计数器
  • 如果候选人未登记,说明他还没有得票

减少票数:纠正错误投票

template<typename Keytype>
int Hashcount<Keytype>::sub(const Keytype& key)
{int idx;if (hash->find(key, idx))  // 确保候选人已登记{return --count[idx];  // 票数减1}return 0;  // 未登记的候选人无法减票
}

应用场景​:发现无效投票需要扣除

  • 只能对已登记的候选人进行操作
  • 确保票数不会变成负数(逻辑上合理)

内存管理:选举结束的清理工作

template<typename Keytype>
Hashcount<Keytype>::~Hashcount()
{delete[] count;  // 拆除所有投票箱if (hash)  // 清理候选人名册{delete hash;hash = NULL;}
}

比喻​:选举结束后的场地清理

  • 拆除所有投票箱和设备
  • 销毁候选人名册记录
  • 释放所有占用资源

设计巧妙的双剑合璧

哈希计数器的精妙之处在于结合了两种数据结构的优势​:

哈希表的优势:

  • 快速查找​:O(1)时间找到候选人对应的投票箱
  • 动态扩展​:自动处理新候选人的登记

数组的优势:

  • 极致速度​:直接索引访问,计数操作极快
  • 内存连续​:缓存友好,访问效率高

协同工作流程:

候选人"张三"投票 → 哈希表查找(张三→5号箱) → 数组count[5]++ → 返回结果

这种设计避免了在哈希表节点中直接存储计数,而是通过映射关系让数组来处理计数,达到了分工合作、各司其职的效果。

实际应用演示

Hashcount<long long> hc(1000);  // 建立可容纳1000个候选人的投票系统hc.add(6);  // 候选人6获得第一票
hc.add(6);  // 候选人6第二票
hc.add(6);  // 候选人6第三票
hc.add(5);  // 新候选人5获得第一票
hc.sub(6);  // 候选人6扣除一票(发现无效票)cout << hc.get(6) << endl;  // 查询候选人6的最终票数:2票
cout << hc.get(5) << endl;  // 查询候选人5的票数:1票

与现实世界的对比

哈希计数器组件选举系统对应物功能描述
hash映射表候选人名册记录候选人到投票箱的对应关系
count数组投票箱集合实际存储每个候选人的得票数
add()操作投票过程为候选人增加票数
get()操作票数查询查看候选人当前得票

性能优势分析

  1. 时间复杂度​:

    • 插入/查询/删除:平均O(1)时间复杂度
    • 远优于遍历查找的O(n)复杂度
  2. 空间效率​:

    • 只为实际出现的键分配空间
    • 数组访问的内存局部性优势
  3. 扩展性​:

    • 轻松支持大量不同的键
    • 动态处理新键的加入

总结

哈希计数器就像是一个智能的选举统计系统,它巧妙地将哈希表的快速查找能力与数组的极致访问速度相结合。这种设计模式在很多实际场景中都有应用:

  • 词频统计​:统计文档中每个单词出现的次数
  • 用户行为分析​:统计每个功能的点击次数
  • 实时投票系统​:大型活动的实时票数统计
  • 数据去重​:结合计数实现高效去重

通过这个生动的选举比喻,相信你已经深刻理解了哈希计数器的工作原理和设计智慧。下次需要统计频率时,你会想起这个高效的"智能投票系统"!

源码及运行

#include<iostream>
#include<string>
#include<unordered_map>
using namespace std;
template<typename Keytype,typename Valutype>
class HashNode
{
public:Keytype key;Valutype valu;HashNode* next;HashNode(const Keytype& key, const Valutype& valu)//传的参数是键和值并且要加引用不能修改传入进来的参数{this->key = key;this->valu = valu;this->next = NULL;}
};
template<typename Keytype, typename Valutype>
class Hashtable
{
private:int size;HashNode<Keytype,Valutype>** table;//跟邻接表有点相似int Hash(const Keytype& key)const{int hashkey = key % size;if (hashkey < 0){hashkey += size;}return hashkey;}
public:Hashtable(int size = 256);~Hashtable();void insert(const Keytype& key, const Valutype& valu);//不希望传进来的键和值被修改void Dlete(const Keytype& key);//键和值是一个整体删除键值就自然删除了bool find(const Keytype& key,Valutype& valu)const;};template<typename Keytype, typename Valutype>
Hashtable<Keytype, Valutype>::Hashtable(int size)
{this->size = size;//改变成员变量给成员size初始化this->table = new HashNode<Keytype, Valutype>* [size];//申请一个大小为size的表for (int i = 0; i < size; i++)//把表头置空,不然就变成野指针了{table[i] = NULL;}}
template<typename Keytype, typename Valutype>
Hashtable<Keytype, Valutype>::~Hashtable()
{for (int i = 0; i < size; i++){if (table[i])//遍历所有表头{HashNode<Keytype, Valutype>* curr = table[i];//申请临时变量来删除表里的同一个键的不同元素while (curr)//{HashNode<Keytype, Valutype>* next = curr->next;delete curr;curr = next;}}table[i] = NULL;//置空防止变成野指针}delete []table;//删除所有表头table = NULL;//给申请数组置空防止变成指针
}
template<typename Keytype, typename Valutype>
void Hashtable<Keytype, Valutype>::insert(const Keytype& key, const Valutype& valu)//插入操作
{int index = Hash(key);//把下标变成键值HashNode<Keytype, Valutype>* now = new HashNode<Keytype, Valutype>(key, valu);//创建新的哈希表节点if (table[index] == NULL)//表头为空则直接把要插入的键置为表头{table[index] = now;}else {//头插法插入表中now->next = table[index];table[index] = now;}}
template<typename Keytype, typename Valutype>
void Hashtable<Keytype, Valutype>::Dlete(const Keytype& key)//键和值是一个整体删除键值就自然删除了
{int index = Hash(key);if (table[key])//哈希表头是否为空,为空就不用删除了{if (table[index] == key)//判断是否是表头{//是表头则将表头的下一个节点保存再next中,删除原来的表头,再把next置为表头HashNode<Keytype, Valutype>* next = table[index]->next;delete table[index];table[index] = next;}else {HashNode<Keytype, Valutype>* curr = table[key];//创建临时变量while (curr->next&&curr->next->key!=key)//遍历表头里面的元素找到要删除元素的前面一个{curr = curr->next;}if (curr->next)//创建临时遍历直接指向要删除的后面的节点,释放掉要删除节点的内存,把被删除的节点置为下一个节点。{HashNode<Keytype, Valutype>* next = curr->next->next;delete curr->next;curr->next = next;}}}
}
template<typename Keytype, typename Valutype>
bool Hashtable<Keytype, Valutype>::find(const Keytype& key,Valutype&valu)const//我传进来的valu会在找到后被修改所以valu前面不用const
{int index = Hash(key);//查找与删除部分逻辑相同,如果是表头就直接将值赋给传进来的键所对应的值然后返回。if (table[index]){if (table[index]->key == key){valu = table[index]->valu;return true;}else {HashNode<Keytype, Valutype>* curr = table[index];while (curr->next && curr->next->key != key){curr = curr->next;}if (curr->next){valu = curr->next->valu;return true;}}}return false;
}
template<typename Keytype>
class Hashcount
{
private:int* count;int countsize;int countindex;Hashtable<Keytype, int>* hash;//int是数组下标
public:Hashcount(int size = 256);~Hashcount();void reset();//重置数据int add(const Keytype& key);//自增操作int get(const Keytype& key);//获取元素int sub(const Keytype& key);//自减操作};template<typename Keytype>//哈希计数器
Hashcount<Keytype>::Hashcount(int size)
{countsize = size;countindex = 0;count = new int[countsize];hash = NULL;reset();}
template<typename Keytype>
Hashcount<Keytype>::~Hashcount()
{delete[]count;if (hash){delete hash;hash = NULL;}
}
template<typename Keytype>
void Hashcount<Keytype>::reset() {if (hash)//存在就先删除,然后再进行重置{delete hash;hash = NULL;}hash = new Hashtable<Keytype, int>(countsize);//申请一个大小为size的哈希表countindex = 0;for (int i = 0; i < countsize; i++){count[i] = 0;//把表置零}}//重置数据
template<typename Keytype>
int Hashcount<Keytype>::add(const Keytype& key)
{int idx;if (!hash->find(key, idx))//找不到就把当前的计数的下标给idx,再把key插入到里面,追后返回count数组自增后的值;{idx = countindex++;hash->insert(key, idx);}
return ++count[idx];}//自增操作
template<typename Keytype>
int Hashcount<Keytype>::get(const Keytype& key)//与删除出入不大,判断存在即可
{int idx;if (hash->find(key, idx)){return count[idx];}return 0;
}//获取元素
template<typename Keytype>
int Hashcount<Keytype>::sub(const Keytype& key)
{int idx;if (hash->find(key, idx)){return --count[idx];//返回删除后的值}return 0;}//自减操作
int main()
{Hashtable<int, char>h(1000);h.insert(1, 'a');h.insert(2, 'b');h.insert(3, 'c');h.insert(545674, 'd');h.insert(1001, 'g');char val;if (!h.find(51, val)){cout << "51 not found" << endl;}if (h.find(545674, val)){cout << "545674 is found is :" << val << endl;}if (h.find(1001, val)){cout << "1001 is found is :" << val << endl;}Hashcount<long long>hc(1000);hc.add(6);hc.add(6);hc.add(6);hc.add(6);hc.add(5);hc.add(6);hc.add(6);hc.sub(6);cout << hc.get(6) << endl;cout << hc.get(5) << endl;return 0;
}

http://www.dtcms.com/a/586320.html

相关文章:

  • 网站侧边栏代码网站设计制作哪种快
  • Flutter SlideTransition 实现平移动画
  • Android EDLA 认证提测前的基本开发和准备简要说明
  • 潍坊网站设计制作seo软件服务
  • 心连网网站wordpress4.9博客模板
  • 数据库要进行分表分库是开发一个项目就要设计好了,还是上线后根据需要再分表分库?
  • TimeBridge: Non-Stationarity Matters for Long-term Time Series Forecasting论文阅读
  • 揭阳网站建设解决方案太原注册公司流程
  • 第2节:程序逻辑与控制流——让程序“思考”
  • 别人网站 自己的二级域名国外家居创意空间设计
  • 东营科技官方网站网站开发入门书籍推荐
  • CSS Grid与Flexbox:2025年响应式布局终极指南
  • 在哪公司建设网站网站优化团队
  • 郑州汉狮哪家做网站好网页设计技巧
  • 【Jenkins 】配置从节点之后,环境配置
  • 如何防范恶意网站PHP手机网站开发工程师
  • Maven项目及Tomcat配置(IDEA)
  • 地方门户网站规划答题小程序开发教程
  • py day34 装饰器
  • 珠海市外贸网站建设公司中美军事的最新消息
  • 制作静态网站当前网站开发用什么软件
  • 天津制作网站wordpress能用手机管理吗
  • Redis面试
  • 网网站建设设计大连弗莱科技官方网站
  • 【个人成长笔记】将Try Ubuntu里面配置好的文件系统克隆在U盘上(创建一个带有持久化功能的Ubuntu Live USB系统)
  • 汤阴县seo快速排名有哪家好seo排名查询工具
  • 常州网站推广培训江宁滨江网站建设
  • 幻灯片网站源码临安网站设计
  • 自己做的游戏
  • 海南住房城乡建设网站淘宝导购网站模板