【C++进阶】哈希 + unordered系列容器

news2024/11/17 13:21:36

在这里插入图片描述

👦个人主页:@Weraphael
✍🏻作者简介:目前学习C++和算法
✈️专栏:C++航路
🐋 希望大家多多支持,咱一起进步!😁
如果文章对你有帮助的话
欢迎 评论💬 点赞👍🏻 收藏 📂 加关注✨


目录

  • 一、哈希思想
  • 二、常见的哈希函数
      • 2.1 直接定址法
      • 2.2 除留余数法
  • 三、哈希冲突
      • 3.1 哈希冲突的原因
      • 3.2 如何解决哈希冲突
        • 闭散列(开放定址法)
        • 开散列(链地址法、开链法、哈希桶)
  • 四、unordered系列
      • 4.1 与map和set的区别
      • 4.2 map/set与unordered_map/unordered_set的性能测试

一、哈希思想

哈希是一种 映射 思想。它是将存储的值跟存储的位置建立映射关系,由这种思想而构成的数据结构称为 哈希表(散列表)

哈希表中插入数据和查找数据 的步骤如下:

  • 插入数据:根据当前待插入的元素的键值,通过哈希函数计算出哈希值,并存入相应的位置中

  • 查找数据:根据待查找元素的键值,计算出哈希值,判断对应的位置中存储的值是否与键值相等

例如:数据集合{1,7,6,4,5,9},哈希函数设置为:hash(key) = key % capacitycapacity为存储元素底层空间总的大小)

在这里插入图片描述

显然,这个哈希表并没有把所有位置都填满,数据分布无序且分散

因此,哈希表又称为散列表

那么如何建立映射关系呢?这就要涉及到哈希函数了。

二、常见的哈希函数

2.1 直接定址法

函数原型:Hash(key) = A * key + BA, B为常数)

  • 适用场景:值的分部范围比较集中。例如:统计字符字符出现的次数。
  • 缺点:需要提前知道键值的分布情况

2.2 除留余数法

函数原型:Hash(key) = key % m m为哈希表的大小)

  • 适用场景:范围不集中,分布分散的数据
  • 缺点:容易出现哈希冲突,需要借助特定方法解决

三、哈希冲突

3.1 哈希冲突的原因

哈希冲突:又称哈希碰撞,不同的值可能会映射到同一个位置。

在这里插入图片描述

如果继续插入元素 44哈希值 hash(44) = 44 % 10 = 4,此时哈希值为 4 的位置处已经有元素了,无法继续存入,此时就发生了 哈希冲突。

在这里插入图片描述

3.2 如何解决哈希冲突

闭散列(开放定址法)

当发生哈希冲突时,如果哈希表未被装满,说明在哈希表中必然还有空位置,那么可以把key存放到冲突位置中的下一个空位置中去。

那么如何寻找下一个空位置呢?

  • 线性探测法:从发生冲突的位置开始,依次向后探测,直到寻找到下一个空位置为止

例如,我们用除留余数法(hash(key)=key%10)将序列{1, 6, 10, 1000, 101, 18, 7, 40}插入到表长为10的哈希表中,插入过程如下:

在这里插入图片描述

通过上图可以看到,随着哈希表中数据的增多,产生哈希冲突的可能性也随着增加,最后在40进行插入的时候更是连续出现了四次哈希冲突。于是如果哈希表接近满的话,插入、查找、删除的效率都会越来越低。

  • 优化方案:二次探测,每次向后探测 i ^ 2。尽管如此,闭散列的效果还是不尽人意,实际中还是 开散列 用的更多一些
开散列(链地址法、开链法、哈希桶)

所谓 开散列 就在原 存储位置 处带上一个 单链表,如果发生 哈希冲突,就将 冲突的值依次挂载即可。因此也叫做 链地址法、开链法、哈希桶。

例如,我们用除留余数法将序列{1, 6, 15, 60, 88, 7, 40, 5, 10}插入到表长为10的哈希表中,当发生哈希冲突时我们采用开散列的形式,将哈希地址相同的元素都链接到同一个哈希桶下,插入过程如下:

在这里插入图片描述

  • 开散列 中进行插入时,如果对应位置的哈希值被占了,那么就在对应位置开一块链表进行存储。
  • 开散列中进行查找时,需要先根据哈希值找到对应位置,并在单链表中进行遍历。

一般情况下,单链表的长度不会太长的,因为扩容后,整体长度会降低。如果单链表真的过长了,我们还可以将其转为红黑树,此时效率依旧非常高。

在这里插入图片描述

  • 值得一提的是 哈希表(开散列法)最快时间复杂度为 O(N),平均是 O(1)

哈希表(开散列法)快排一样很特殊,时间复杂度不看最坏的,看 平均时间复杂度,因为 最快的情况几乎不可能出现

四、unordered系列

4.1 与map和set的区别

哈希表最厉害的地方在于 查找速度非常快,比红黑树还快,时间复杂度是O(1)(后面的性能测试见)。因此在 C++11 标准中,利用 哈希表 作为底层结构,重写了 set / map,就是 unordered_set / unordered_map

unordered系列的使用和map以及set几乎没区别,使用方面建议大家查文档:点击跳转

要说有区别如下:

在这里插入图片描述

4.2 map/set与unordered_map/unordered_set的性能测试

说到一个容器的性能,我们最关心的实际就是该容器增删查改的效率。我们可以通过下列代码测试set容器和unordered_set容器insertfind以及erase的效率。

【测试代码】

#include <iostream>
#include <unordered_set>
#include <unordered_map>
#include <set>
#include <map>
#include <vector>
using namespace std;


void Performance_testing()
{
    // set和unordered_set
    const int N = 1000000; // 一百万

    set<int> s;
    unordered_set<int> us;

    vector<int> v;
    v.reserve(N);
    srand(time(0));

    for (int i = 0; i < N; i++)
    {
        v.push_back(rand());
        // v.push_back(rand() + i);
        // v.push_back(i);
    }

    // =========== 插入测试 =================

    size_t begin1 = clock();
    for (auto e : v)
    {
        s.insert(e);
    }
    size_t end1 = clock();

    cout << "set的插入时间:" << end1 - begin1 << endl;

    size_t begin2 = clock();
    for (auto e : v)
    {
        us.insert(e);
    }
    size_t end2 = clock();
    cout << "unordered_set的插入时间:" << end2 - begin2 << endl;

    // =========== 查找测试 =================
    size_t begin3 = clock();
    for (auto e : v)
    {
        s.find(e);
    }
    size_t end3 = clock();
    cout << "set的find:" << end1 - begin1 << endl;

    size_t begin4 = clock();
    for (auto e : v)
    {
        us.find(e);
    }
    size_t end4 = clock();
    cout << "unordered_set的find:" << end4 - begin4 << endl;


    cout << "set插入数据个数:" << s.size() << endl;
    cout << "unordered_set插入数据个数:" << us.size() << endl;

    // =========== 删除测试 =================
    size_t begin5 = clock();
    for (auto e : v)
    {
        s.erase(e);
    }
    size_t end5 = clock();
    cout << "set删除:" << end5 - begin5 << endl;

    size_t begin6 = clock();
    for (auto e : v)
    {
        us.erase(e);
    }
    size_t end6 = clock();
    cout << "unordered_set删除:" << end6 - begin6 << endl;
}


int main()
{
    Performance_testing();
	
	return 0;
}
  • 1000个数做增删查改操作

在这里插入图片描述

我们发现:当数据量小的时候,它们的效率差不多。

  • 10000000个数做增删查改操作

在这里插入图片描述

当数据量达到一千万的时候,明显unordered系列快多了。

根据测试结果可以得出以下结论:

  • 当处理数据量小时,map/set容器与unordered系列容器增删查改的效率差异不大。

  • 当处理数据量大时,map/set容器与unordered系列容器增删查改的效率相比,unordered系列容器的效率更高。

因此,如果在unordered系列和map/set容器,应该首选unordered系列; 另外如果需要存储的序列为有序时,应该选用map/set容器。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1476444.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

前端css、js、bootstrap、vue2.x、ajax查漏补缺(1)

学到的总是忘&#xff0c;遇到了就随手过来补一下 1.【JS】innerHTML innerHTML属性允许更改HTML元素的内容可以解析HTML标签 2.【CSS】display: none 设置元素不可见&#xff0c;不占空间&#xff0c;约等于将元素删除一样&#xff0c;只是源代码还存在 3.【CSS】行内样式 4.【…

机器学习专项课程03:Unsupervised Learning, Recommenders, Reinforcement Learning笔记 Week01

Week 01 of Unsupervised Learning, Recommenders, Reinforcement Learning 本笔记包含字幕&#xff0c;quiz的答案以及作业的代码&#xff0c;仅供个人学习使用&#xff0c;如有侵权&#xff0c;请联系删除。 课程地址&#xff1a; https://www.coursera.org/learn/unsupervi…

前端视角对Rust的浅析

概述 本文将从 Rust 的历史&#xff0c;前端的使用场景和业界使用案例一步步带你走进 Rust的世界。并且通过一些简单的例子&#xff0c;了解 Rust 如何应用到前端&#xff0c;提高前端的生产效率。 Rust简史 2006年&#xff0c;软件开发者Graydon Hoare在Mozilla工作期间&#…

单细胞Seurat - 细胞聚类(3)

本系列持续更新Seurat单细胞分析教程&#xff0c;欢迎关注&#xff01; 维度确定 为了克服 scRNA-seq 数据的任何单个特征中广泛的技术噪音&#xff0c;Seurat 根据 PCA 分数对细胞进行聚类&#xff0c;每个 PC 本质上代表一个“元特征”&#xff0c;它结合了相关特征集的信息。…

【三维重建】【slam】【分块重建】LocalRF:逐步优化的局部辐射场的鲁棒视图合成

项目地址&#xff1a;https://localrf.github.io/ 题目&#xff1a;Progressively Optimized Local Radiance Fields for Robust View Synthesis 来源&#xff1a;KAIST、National Taiwan University、Meta 、University of Maryland, College Park 提示&#xff1a;文章用了s…

学习Android的第十八天

目录 Android 可复用 BaseAdapter 为什么使用BaseAdapter&#xff1f; 如何使用BaseAdapter&#xff1f; Android GridView 网格视图 GridView 属性 示例 Android Spinner 下拉选项框 Spinner Spinner 属性 示例 Android AutoCompleteTextView 自动完成文本框 Auto…

观成科技:加密C2框架Covenant流量分析

工具介绍 Covenant是一个基于.NET的开源C2服务器&#xff0c;可以通过HTTP/HTTPS 控制Covenant agent&#xff0c;从而实现对目标的远程控制。Covenant agent在与C2通信时&#xff0c;使用base64/AES加密载荷的HTTP隧道构建加密通道。亦可选择使用SSL/TLS标准加密协议&#xf…

【C/C++】inline内联函数详解

创作不易&#xff0c;本篇文章如果帮助到了你&#xff0c;还请点赞 关注支持一下♡>&#x16966;<)!! 主页专栏有更多知识&#xff0c;如有疑问欢迎大家指正讨论&#xff0c;共同进步&#xff01; &#x1f525;c系列专栏&#xff1a;C/C零基础到精通 &#x1f525; 给大…

使用drawio画数据库实体关系图

在drawio中使用数据库实体关系图 drawio是一款强大的图表绘制软件&#xff0c;支持在线云端版本以及windows, macOS, linux安装版。 如果想在线直接使用&#xff0c;则直接输入网址drawon.cn或者使用drawon(桌案), drawon.cn内部完整的集成了drawio的所有功能&#xff0c;并实现…

如何利用ChatGPT搞科研?论文检索、写作、基金润色、数据分析、科研绘图(全球地图、植被图、箱型图、雷达图、玫瑰图、气泡图、森林图等)

以ChatGPT、LLaMA、Gemini、DALLE、Midjourney、Stable Diffusion、星火大模型、文心一言、千问为代表AI大语言模型带来了新一波人工智能浪潮&#xff0c;可以面向科研选题、思维导图、数据清洗、统计分析、高级编程、代码调试、算法学习、论文检索、写作、翻译、润色、文献辅助…

【STM32】STM32学习笔记-WDG看门狗(46)

00. 目录 文章目录 00. 目录01. WDG简介02. IWDG概述03. IWDG框图04. IWDG键寄存器05. WWDG简介06. WWDG框图07. WWDG工作特性08. IWDG和WWDG对比09. 预留10. 附录 01. WDG简介 WDG&#xff08;Watchdog&#xff09;看门狗 看门狗可以监控程序的运行状态&#xff0c;当程序因为…

ubuntu20.04安装docker及运行

ubuntu20.04安装docker及运行 ubuntu环境版本 Ubuntu Focal 20.04 (LTS) 查看系统版本 rootubuntu20043:~# cat /proc/version Linux version 5.15.0-78-generic (builddlcy02-amd64-008) (gcc (Ubuntu 11.3.0-1ubuntu1~22.04.1) 11.3.0, GNU ld (GNU Binutils for Ubuntu) …

基于Eclipse+Tomcat+Mysql开发的网络考试系统的设计与实现

基于EclipseTomcatMysql开发的网络考试系统的设计与实现 项目介绍&#x1f481;&#x1f3fb; 网络考试系统主要用于实现高校在线考试&#xff0c;基本功能包括&#xff1a;自动组卷、试卷发布、试卷批阅、试卷成绩统计等。本系统结构如下&#xff1a; &#xff08;1&#xff0…

C++重点---STL简介

顾得泉&#xff1a;个人主页 个人专栏&#xff1a;《Linux操作系统》 《C从入门到精通》 《LeedCode刷题》 键盘敲烂&#xff0c;年薪百万&#xff01; 一、STL简介 STL&#xff08;Standard Template Library&#xff09;是C标准库中的一个重要组成部分&#xff0c;它提供了…

8 easy 14. 最长公共前缀

纵向扫描法&#xff1a; //编写一个函数来查找字符串数组中的最长公共前缀。 // // 如果不存在公共前缀&#xff0c;返回空字符串 ""。 // // // // 示例 1&#xff1a; // // //输入&#xff1a;strs ["flower","flow","flight"…

Keras 3.0发布:全面拥抱 PyTorch!

Keras 3.0 介绍 https://keras.io/keras_3/ Keras 3.0 升级是对 Keras 的全面重写&#xff0c;引入了一系列令人振奋的新特性&#xff0c;为深度学习领域带来了全新的可能性。 多框架支持 Keras 3.0 的最大亮点之一是支持多框架。Keras 3 实现了完整的 Keras API&#xff0c;…

面试笔记系列八之JVM基础知识点整理及常见面试题

类实例化加载顺序 加载&#xff1a;当程序访问某个类时&#xff0c;JVM会首先检查该类是否已经加载到内存中。如果尚未加载&#xff0c;则会进行加载操作。加载操作将类的字节码文件加载到内存&#xff0c;并为其创建一个Class对象。 连接&#xff08;验证、准备、解析&#x…

Qt程序设计-指南针自定义控件实例

本文讲解Qt指南针自定义控件实例。 效果演示 创建指南针类 #ifndef COMPASS_H #define COMPASS_H#include <QWidget> #include <QWidget> #include <QTimer> #include <QPainter> #include <QPen> #include <QDebug> #include <QtMat…

【MATLAB】REMD_ MFE_SVM_LSTM 神经网络时序预测算法

有意向获取代码&#xff0c;请转文末观看代码获取方式~也可转原文链接获取~ 1 基本定义 REMD_MFE_SVM_LSTM神经网络时序预测算法是一种结合了REMD&#xff08;Reservoir Enhanced Multi-scale Deep Learning&#xff09;算法、多尺度特征提取&#xff08;MFE&#xff09;、支持…

许多主要新闻媒体正屏蔽 OpenAI 爬虫

自OpenAI的内容生成式人工智能模型面世以来&#xff0c;大量互联网数据成为了不断训练和优化模型的“饵料”&#xff0c;但据路透社研究所的一项调查&#xff0c;有越来越多的新闻媒体已对OpenAI的数据爬取说“不”&#xff0c;在传统媒体领域&#xff0c;这一比例甚至超过了50…