基于哈希的 map 和 set

简述

基于哈希的 mapset ,它们分别叫做 unordered_map, unordered_set 。数据分布越平均,性能相较 mapset 来说提升就更大。但由于它们基于哈希,所以并不像 mapset 一样能自动排序;它们都是无序的。

我做了一个测试:随机生成 \(10^7\) 个 int 范围内的整数(平均分布),然后将其分别插入 mapunordered_map,再完整的做一次查询,查看时间和内存上的消耗。

测试代码

#include <cstdio>
#include <cstdlib>
#include <ctime>
#include <map>
#include <vector>
#include <unordered_map> using namespace std; vector<pair<int, int>> data; int main() {
freopen("map.txt", "r", stdin);
data.resize(5000000);
map<int, int> mp;
double st, ed;
st = clock();
for (int i = 0; i < 5000000; ++i) scanf("%d%d", &data[i].first, &data[i].second);
ed = clock();
printf("Read: %dms\n", int(1000.0 * (ed - st) / CLOCKS_PER_SEC));
st = clock();
for (int i = 0; i < 5000000; ++i) mp[data[i].first] = data[i].second;
ed = clock();
printf("Insert: %dms\n", int(1000.0 * (ed - st) / CLOCKS_PER_SEC));
st = clock();
for (int i = 0; i < 5000000; ++i) data[i].second = mp[data[i].first];
ed = clock();
printf("Query: %dms\n", int(1000.0 * (ed - st) / CLOCKS_PER_SEC));
data = vector<pair<int, int>>();
}

测试结果

结构 总耗时 插入耗时 查询耗时 内存
map 18,041 MS 10,299 MS 7,742 MS 230.7 MB
unordered_map 7,138 MS 5,426 MS 1,712 MS 212.0 MB

当数据分布平均时,从时间上看,两者的性能差距约为 \(7138 / 18041 \approx 40\%\)

提示

即使在平均意义下 unordered_map 有着比 map 更高的效率,但是由于两者的实现(前者哈希,后者平衡树), unordered_map 单次插入的最差复杂度是 \(O(N)\), map 单次插入的最差复杂度是 \(O(log N)\) 。所以在出题人故意造数据卡 unordered_map 使其内部哈希表发生严重冲突时,unordered_map 就会变得很慢。

语法上的变化

auto 关键字的新含义

在 C++11 中,auto 关键字被赋予了新的含义。其功能是自动类型推导。

auto x = 3; // y的类型自动推导为 int
auto y = make_pair(1, 2); // y的类型自动推导为 pair<int, int>
auto z; // 编译错误, z的类型需要一个初始值才能推导 map<int, int> mp;
// iter的类型推导为map<int, int>::iterator
for (auto iter = mp.begin(); iter != mp.end(); ++iter)
cout << iter->first << ' ' << iter->second << '\n';

与其相关的是一个新的关键字 decltype,取得某一个值的类型。

int a = 1, b = 2;
decltype(a + b) c; // c为int

增强的 for(基于范围的for循环)

map<int, int> mp;

// C++11 之前遍历
for (map<int, int>::iterator it = mp,begin(); it != mp,end(); ++it)
cout << it->first << ' ' << it->second << '\n'; // C++11 之后
for (auto val : mp)
cout << val.first << ' ' << val.second << '\n';

模板嵌套时一个小改善

priority_queue<pair<int, int> > heap; // C++11之前需加一个空格避免歧义
priority_queue<pair<int, int>> heap; // C++11之后不用

初始化列表

更简单的初始化方式。

vector<pair<int, int>> v{ {1, 2}, {2, 3}, {3, 4} };

// 等价于下面代码
vector<pair<int, int> > v;
v.push_back(make_pair(1, 2));
v.push_back(make_pair(2, 3));
v.push_back(make_pair(3, 4));

lambda 表达式

下面用一个例子讲述一下lambda表达式的大致用法。

#include <iostream>
#include <vector>
#include <algorithm> using namespace std; int main() {
vector<pair<int, int>> v{ {1, 2}, {2, 3}, {3, 4} };
for (auto& val : v) {
cout << val.first << ' ' << val.second << '\n';
}
sort(v.begin(), v.end(), [](const pair<int, int> &a, const pair<int, int> &b) {
return a.first > b.first;
});
cout << "After sort:\n";
for (auto& val : v) {
cout << val.first << ' ' << val.second << '\n';
}
return 0;
}

代码里的 sort,在C++11之前比较常见的等价写法是:

bool cmp(const pair<int, int> &a, const pair<int, int> &b) {
return a.first > b.first;
} int main() {
// ...
sort(v.begin(), v.end(), cmp);
// ...
}

C++11 新用法的更多相关文章

  1. C++11之for循环的新用法《转》

    相关资料:https://legacy.gitbook.com/book/changkun/cpp1x-tutorial/details C++11之for循环的新用法 C++使用如下方法遍历一个容器 ...

  2. C++11常用特性介绍——for循环新用法

    一.for循环新用法——基于范围的for循环 for(元素类型 元素对象 : 容器对象) { //遍历 } 1)遍历字符串 std::string str = "hello world&qu ...

  3. C++ 11学习和掌握 ——《深入理解C++ 11:C++11新特性解析和应用》读书笔记(一)

    因为偶然的机会,在图书馆看到<深入理解C++ 11:C++11新特性解析和应用>这本书,大致扫下,受益匪浅,就果断借出来,对于其中的部分内容进行详读并亲自编程测试相关代码,也就有了整理写出 ...

  4. [转载] C++11新特性

    C++11标准发布已有一段时间了, 维基百科上有对C++11新标准的变化和C++11新特性介绍的文章. 我是一名C++程序员,非常想了解一下C++11. 英文版的维基百科看起来非常费劲,而中文版维基百 ...

  5. c++11新标准for循环和lambda表达式

    :first-child { margin-top: 0px; } .markdown-preview:not([data-use-github-style]) h1, .markdown-previ ...

  6. C++11新特性之一——Lambda表达式

    C++11新特性总结可以参考:http://www.cnblogs.com/pzhfei/archive/2013/03/02/CPP_new_feature.html#section_6.8 C++ ...

  7. C++11新标准:nullptr关键字

    一.nullptr的意义 1.NULL在C中的定义 #define NULL (void*)0 2.NULL在C++中的定义 #ifndef NULL #ifdef __cplusplus #defi ...

  8. C++11新标准:decltype关键字

    一.decltype意义 有时我们希望从表达式的类型推断出要定义的变量类型,但是不想用该表达式的值初始化变量(如果要初始化就用auto了).为了满足这一需求,C++11新标准引入了decltype类型 ...

  9. C++11新标准:auto关键字

    一.auto意义 编程时常常需要把表达式的值赋给变量,这就要求在声明变量的时候清楚地知道表达式的类型,然后要做到这一点并非那么容易.为了解决这个问题,C++11新标准引入了auto类型说明符,用它就能 ...

随机推荐

  1. if 语句 总结笔记

    1.if 语句 语法: if(condition) statement1; else statement2; graph TD A[JAVA考试] -->|几天后| B(收到成绩单) B --& ...

  2. w13scan扫描器的使用

    0x01 w13scan第三方包下载 环境:python3以上 下载:pip install w13scan 0x02 利用w13scan API接口编写w13scan.py from W13SCAN ...

  3. 有多少人在面试时,被Java 如何线程间通讯,问哭了?

    正常情况下,每个子线程完成各自的任务就可以结束了.不过有的时候,我们希望多个线程协同工作来完成某个任务,这时就涉及到了线程间通信了. 本文涉及到的知识点: thread.join(), object. ...

  4. java多线程技能-使用多线程-继承Thread类

    /* 使用多线程可通过继承Thread类或实现Runnable接口. Thread和Runnable的关系:public class Thread implements Runnable. 使用thr ...

  5. linux版本的jdk1.8+hadoop2.9.2下载地址

    hadoop: 链接:https://pan.baidu.com/s/14AhhPYP8933tn-EfSX-i8Q 提取码:e90m jdk1.8: 链接:https://pan.baidu.com ...

  6. 【笔记】机器学习 - 李宏毅 - 13 - Why Deep

    当参数一样多的时候,神经网络变得更高比变宽更有效果.为什么会这样呢? 其实和软件行业的模块化思想是一致的. 比如,如果直接对这四种分类进行训练,长发的男孩数据较少,那么这一类训练得到的classifi ...

  7. 在csv表格中修改/追加某行数据

    思路: 文本文件不能随意穿插信息,但是通过使用Seek()方法,可以在读取文本文件中移动光标从而修改所要修改的行. 思路步骤: 1.读取文件,打开csv文件,获取文件流,seek移动光标到开始, fo ...

  8. markdown转成word或者pdf

    利用typora软件 1.登陆官网下载软件 官网地址:https://typora.io/ 点击download 根据自己的电脑下载64位或者32位 2.安装软件 安装界面如下: 3.转换 3.1首先 ...

  9. gulp常用插件之gulp-plumber使用

    更多gulp常用插件使用请访问:gulp常用插件汇总 gulp-plumber这是一款防止因gulp插件的错误而导致管道中断,plumber可以阻止 gulp 插件发生错误导致进程退出并输出错误日志. ...

  10. centos8 samba

    安装dnf install -y samba samba-client开机启动systemctl enable smb立即启动systemctl start smb防火墙放行firewall-cmd ...