【算法】哈希映射(C/C++)

简介: 【算法】哈希映射(C/C++)

哈希映射算法是一种通过哈希函数将键映射到数组索引以快速访问数据的数据结构。它的核心思想是利用哈希函数的快速计算能力,将键(Key)转换为数组索引,从而实现对数据的快速访问和存储。哈希映射在现代软件开发中非常重要,它提供了高效的数据查找、插入和删除操作。

算法引入:

小白算法学校今年经过了层层考核,预选拔了六名队员,这六名队员的信息如下,好巧不巧的是这六名队员有重名的,叫张三的有两个人,叫李四的有两个人。那么教练如何区分它们呢,只叫队员的名字很可能叫错人,这是不被认可的叫法,根据下标去叫张三,可是真正的张三都不知道自己的下标是哪一个。欣慰的时,每一名队员都有一个学号,学号是唯一的值,每一名队员的学号都不一样,那么教练可以直接说出学号的值,那么就可以锁定唯一的那一位队员了,此时去用下标对应学号,那么唯一的下标值对应唯一且不重复的学号,那么这就是叫做哈希映射,下标被称为“键”,学号被称为“值”。

下标 姓名 学号
0 张三 100
1 李四 101
2 王五 102
3 赵六 103
4 张三 107
5 李四 108

算法介绍:

哈希映射在绝大部分题目中使用的频率还是比较高的,它主要的思想就是键值映射,通过一个整数也就是下标值,在一个数组里面有且仅有一个唯一的值与之对应,有点类似于经过去重的数组一样,但是这种映射是有规律可循的。

哈希映射的工作原理依赖于哈希函数,哈希函数接受一个键作为输入,并返回一个值,这个整数通常用作数组的索引。理想情况下,哈希函数应该将输入均匀分布到所有可能的索引值上,以减少不同键映射到同一个索引值的情况,即“哈希碰撞”。

当发生哈希碰撞时,有几种常见的解决策略

1. 链地址法:每个数组元素不直接存储键值对,而是存储一个链表。当多个键通过哈希函数映射到同一索引时,这些键值对将被存储在同一个链表中。

2. 开放寻址法:当发生哈希碰撞时,哈希映射会尝试找到数组中的下一个空闲位置,按照某种系统的方式(如线性探测)进行。

优点:

哈希映射的优点就是数据操作增删改查,增加、删除、查找操作的时间复杂度接近O(1)。它还支持动态扩容,以适应数据量的增加。

缺点:

哈希映射也有一些缺点,比如哈希碰撞虽然可以减少但无法完全避免,需要通过额外的数据结构或探测算法来解决。此外,为了减少哈希碰撞,哈希表可能会预留较大的空间,导致内存利用率不是很高。

哈希映射实现:

哈希映射实现方式一般都是通过map来实现,map就类似上述的功能,可以说是一个数组,但是里面的下标可以是数字、字符、字符串、pair、结构体等,这些下标去映射值,值也有可能数字、字符、字符串、pair、结构体等,map內部的实现自建一颗红黑树,map的查询、插入、删除操作的时间复杂度都是O(logn)。map默认按照key进行升序排序,和输入的顺序无关。 如果是int/double等数值型为key,那么就按照大小排列;如果是string类型,那么就按照字符串的字典序进行排列。

map

在使用map时,需要加入头文件#include<map>,下面解析一下map常用的函数:

1.insert

insert是插入函数,在指定的下标位置插入键值映射。

mymap.insert(mymap.begin(),pair<char,int>('a',1));//指定位置插入
mymap.insert(pair<char,int>('a',1));//直接插入键值对
mymap.insert({{'a',1},{'b',2}});//多键值对插入
//相当于mymap['a']=1;


2.find

find为查找函数,如果查找到则返回迭代器指向当前查找元素的位置,否则查找不到返回map.end()位置。

map<char,int>::iterator it=mymap.find('a');//查找关键字为'a'的迭代器位置

3.erase

erase是删除函数,能够对指定位置进行删除键值,也可以根据键进行删除,或者通过迭代器进行删除。

map<char,int>::iterator it=mymap.find('a');//查找关键字为'a'的迭代器位置
mymap.erase(it);//对map容器的第二个位置进行删除键值映射
mymap.erase('a');//删除('a',1)键值映射,删除成功能找到'a'的键,返回1,否则返回0
mymap.erase(mymap.begin(),mymap.end());//删除整个map容器,功能相当于clear

4.clear

clear函数则是清空函数,对整个map容器进行清空,里面不再有键值映射关系。

mymap.clear();

5.遍历

map的遍历有很多种,但是最常见的还是迭代器遍历,迭代器遍历非常方便,可以很好的操作map容器,其次使用range遍历,前提是C++11的编译器,其特点是简洁。

  map<char,int>::iterator iter;
  for(iter=mymap.begin();iter!=mymap.end();iter++){//迭代器遍历
    cout<<iter->first<<" "<<iter->second;
  }
  for(auto it1:mymap){//range遍历
    cout<<it1.first<<" "<<it1.second;
  }

其他

下面是C++ map其他的一些函数。

     begin()         返回指向map头部的迭代器
     end()           返回指向map末尾的迭代器
     count()         返回指定元素出现的次数,因为key值不会重复,所以非0即1
     empty()         判空,map为空则返回true
     max_size()      返回可以容纳的最大元素个数
     rbegin()        返回一个指向map尾部的逆向迭代器
     rend()          返回一个指向map头部的逆向迭代器
     size()          返回map中元素的个数
     swap()          交换两个map里面的键值
     upper_bound()   返回键值>给定元素的第一个位置
     lower_bound()   返回键值>=给定元素的第一个位置
     value_comp()    返回比较元素value的函数
     key_comp()      返回比较元素key的函数
#include<iostream>
#include<map>
using namespace std;
map<char,int> mymap;
int main(){
  mymap.insert(mymap.begin(),pair<char,int>('a',1));//指定位置插入
  mymap.insert(pair<char,int>('a',1));//直接插入键值对
  mymap.insert({{'a',1},{'b',2}});//多键值对插入
  //相当于mymap['a']=1;
  map<char,int>::iterator it=mymap.find('a');//查找关键字为'a'的迭代器位置
  mymap.erase(it);//对map容器的第二个位置进行删除键值映射
  mymap.erase('a');//删除('a',1)键值映射,删除成功能找到'a'的键,返回1,否则返回0
  mymap.erase(mymap.begin(),mymap.end());//删除整个map容器,功能相当于clear
  
  map<char,int>::iterator iter;
  for(iter=mymap.begin();iter!=mymap.end();iter++){//迭代器遍历
    cout<<iter->first<<" "<<iter->second;
  }
  for(auto it1:mymap){//range遍历
    cout<<it1.first<<" "<<it1.second;
  }
  
  if(!mymap.empty()){//empty函数
    cout<<1<<endl;
  }
  map<char,int> youmap;
  swap(mymap,youmap);//swap函数 map交换
  
  if(!mymap.count('a')){//count函数
    cout<<1<<endl;
  }
  
  auto it2=mymap.lower_bound(1);//lower_bound函数
  
  map<char,int>::key_compare comp = mymap.key_comp();//key_comp()函数
    bool result = comp('a', 'b');
    cout <<result<<endl;  // 输出 1 (true)
 
  return 0;
}

unordered_map

unordered_map和map类似,都是存储的key-value的值,可以通过key快速索引到value。不同的是unordered_map不会根据key的大小进行排序,存储时是根据key的hash值判断元素是否相同,即unordered_map内部元素是无序的。unordered_map的底层是一个防冗余的哈希表(开链法避免地址冲突)。unordered_map用到自定义的类型,需要对key定义hash_value函数并且重载operator == 哈希表最大的优点,就是把数据的存储和查找消耗的时间大大降低,时间复杂度为O(1),函数基本都一样。

题目链接:“蓝桥杯”练习系统

问题描述

  给定2维平面上n个整点的坐标,一条直线最多能过几个点?

输入格式

  第一行一个整数n表示点的个数

  以下n行,每行2个整数分别表示每个点的x,y坐标。

输出格式

  输出一个整数表示答案。

样例输入

5

0 0

1 1

2 2

0 3

2 3

样例输出

3

数据规模和约定

  n<=1500,数据保证不会存在2个相同的点。

  点坐标在int范围内

解析:

这是妥妥的map的映射啊,这个是斜率double类型去映射int类型的直线点的个数,用哈希表关键字与键值映射即可,斜率当关键字,每有一个点在此直线上不断加一即可。话不多说,直接上代码,代码附注释。

代码实现:

#include<iostream>
#include<utility>
#include<map>
 
using namespace std;
 
pair<int,int> a[2000];//输入二维坐标,用pair或结构体
int n,maxx;
 
int main(){
    cin>>n;
    for(int i=0;i<n;i++){
        cin>>a[i].first>>a[i].second;//输入位置
    }
    map<double,int> p;//用map映射
    for(int i=0;i<n;i++){//始点
        for(int j=i+1;j<n;j++){//终点
            double k;//斜率
            k=double(a[i].second-a[j].second)/(a[i].first-a[j].first);//强转为double,否则数据会四舍五入
            if(p[k]==0){//如果此次斜率是第一次出现,说明两点构成一条直线,点数为2,需要多加一次
                p[k]++;
            }
            p[k]++;//如果存在了那就再此基础上加一即可
            maxx=max(p[k],maxx);//寻找最大值
        }
        p.erase(p.begin(),p.end());//暴力枚举,若不清空,会影响下一个始点的计算
        //我们是先固定起点,再遍历终点,求斜率
        //若有三个点构成一条直线,第一次固定第一个点为始点,终点遍历后面两个点,此时为点数3
        //若不清空继续遍历,第二个点为始点,第三个点为终点,p[k]++还会继续,变为4,重复计算,所以清空
    }
    cout<<maxx<<endl;
    return 0;
}

哈希映射算法应用的场景还是非常多的,博主在参加第十五届蓝桥杯国赛的时候,80%的题目都用到了map映射,博主感觉国赛考察map映射能力去处理、解决问题还是比较重要的。执笔至此,感触彼多,全文将至,落笔为终,感谢各位的支持。

相关文章
|
6月前
|
存储 监控 算法
基于 C++ 哈希表算法实现局域网监控电脑屏幕的数据加速机制研究
企业网络安全与办公管理需求日益复杂的学术语境下,局域网监控电脑屏幕作为保障信息安全、规范员工操作的重要手段,已然成为网络安全领域的关键研究对象。其作用类似网络空间中的 “电子眼”,实时捕获每台电脑屏幕上的操作动态。然而,面对海量监控数据,实现高效数据存储与快速检索,已成为提升监控系统性能的核心挑战。本文聚焦于 C++ 语言中的哈希表算法,深入探究其如何成为局域网监控电脑屏幕数据处理的 “加速引擎”,并通过详尽的代码示例,展现其强大功能与应用价值。
157 2
|
8月前
|
存储 负载均衡 算法
基于 C++ 语言的迪杰斯特拉算法在局域网计算机管理中的应用剖析
在局域网计算机管理中,迪杰斯特拉算法用于优化网络路径、分配资源和定位故障节点,确保高效稳定的网络环境。该算法通过计算最短路径,提升数据传输速率与稳定性,实现负载均衡并快速排除故障。C++代码示例展示了其在网络模拟中的应用,为企业信息化建设提供有力支持。
247 15
|
8月前
|
存储 算法 数据处理
公司局域网管理中的哈希表查找优化 C++ 算法探究
在数字化办公环境中,公司局域网管理至关重要。哈希表作为一种高效的数据结构,通过哈希函数将关键值(如IP地址、账号)映射到数组索引,实现快速的插入、删除与查找操作。例如,在员工登录验证和设备信息管理中,哈希表能显著提升效率,避免传统线性查找的低效问题。本文以C++为例,展示了哈希表在局域网管理中的具体应用,包括设备MAC地址与IP分配的存储与查询,并探讨了优化哈希函数和扩容策略,确保网络管理高效准确。
|
4月前
|
存储 监控 算法
基于跳表数据结构的企业局域网监控异常连接实时检测 C++ 算法研究
跳表(Skip List)是一种基于概率的数据结构,适用于企业局域网监控中海量连接记录的高效处理。其通过多层索引机制实现快速查找、插入和删除操作,时间复杂度为 $O(\log n)$,优于链表和平衡树。跳表在异常连接识别、黑名单管理和历史记录溯源等场景中表现出色,具备实现简单、支持范围查询等优势,是企业网络监控中动态数据管理的理想选择。
147 0
|
6月前
|
监控 算法 数据处理
基于 C++ 的 KD 树算法在监控局域网屏幕中的理论剖析与工程实践研究
本文探讨了KD树在局域网屏幕监控中的应用,通过C++实现其构建与查询功能,显著提升多维数据处理效率。KD树作为一种二叉空间划分结构,适用于屏幕图像特征匹配、异常画面检测及数据压缩传输优化等场景。相比传统方法,基于KD树的方案检索效率提升2-3个数量级,但高维数据退化和动态更新等问题仍需进一步研究。未来可通过融合其他数据结构、引入深度学习及开发增量式更新算法等方式优化性能。
181 17
|
6月前
|
存储 监控 算法
内网监控桌面与 PHP 哈希算法:从数据追踪到行为审计的技术解析
本文探讨了内网监控桌面系统的技术需求与数据结构选型,重点分析了哈希算法在企业内网安全管理中的应用。通过PHP语言实现的SHA-256算法,可有效支持软件准入控制、数据传输审计及操作日志存证等功能。文章还介绍了性能优化策略(如分块哈希计算和并行处理)与安全增强措施(如盐值强化和动态更新),并展望了哈希算法在图像处理、网络流量分析等领域的扩展应用。最终强调了构建完整内网安全闭环的重要性,为企业数字资产保护提供技术支撑。
193 2
|
存储 算法 Java
解析HashSet的工作原理,揭示Set如何利用哈希算法和equals()方法确保元素唯一性,并通过示例代码展示了其“无重复”特性的具体应用
在Java中,Set接口以其独特的“无重复”特性脱颖而出。本文通过解析HashSet的工作原理,揭示Set如何利用哈希算法和equals()方法确保元素唯一性,并通过示例代码展示了其“无重复”特性的具体应用。
234 3
|
5月前
|
机器学习/深度学习 存储 算法
基于 C++ 布隆过滤器算法的局域网上网行为控制:URL 访问过滤的高效实现研究
本文探讨了一种基于布隆过滤器的局域网上网行为控制方法,旨在解决传统黑白名单机制在处理海量URL数据时存储与查询效率低的问题。通过C++实现URL访问过滤功能,实验表明该方法可将内存占用降至传统方案的八分之一,查询速度提升约40%,假阳性率可控。研究为优化企业网络管理提供了新思路,并提出结合机器学习、改进哈希函数及分布式协同等未来优化方向。
160 0
|
7月前
|
存储 监控 算法
基于 C++ 哈希表算法的局域网如何监控电脑技术解析
当代数字化办公与生活环境中,局域网的广泛应用极大地提升了信息交互的效率与便捷性。然而,出于网络安全管理、资源合理分配以及合规性要求等多方面的考量,对局域网内计算机进行有效监控成为一项至关重要的任务。实现局域网内计算机监控,涉及多种数据结构与算法的运用。本文聚焦于 C++ 编程语言中的哈希表算法,深入探讨其在局域网计算机监控场景中的应用,并通过详尽的代码示例进行阐释。
161 4
|
8月前
|
存储 算法 安全
企业员工数据泄露防范策略:基于 C++ 语言的布隆过滤器算法剖析[如何防止员工泄密]
企业运营过程中,防范员工泄密是信息安全领域的核心议题。员工泄密可能致使企业核心数据、商业机密等关键资产的流失,进而给企业造成严重损失。为应对这一挑战,借助恰当的数据结构与算法成为强化信息防护的有效路径。本文专注于 C++ 语言中的布隆过滤器算法,深入探究其在防范员工泄密场景中的应用。
187 8
下一篇
oss云网关配置