基于哈希的 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. C#中StreamReader类读取文件使用示例

    C#中StreamReader类读取文件使用示例 1.需要导入的命名空间是:System.IO; 2.操作的是字符,所以打开的是文本文件. 常用属性:   CurrentEncoding:对象正在使用 ...

  2. 【小白视频学Java for循环】3分钟学会Java的for循环,让看懂for循环嵌套再不是难事

    目录 一.单个for循环介绍 二.for循环嵌套 听讲时能听懂的for循环为什么一做题就晕菜?一个for循环还勉强能看懂,但为什么一看到双重for循环脑子里就感觉脑子全是浆糊? 如果有上述问题那么就继 ...

  3. 【daily】文件分割限速下载,及合并分割文件

    说明 主要功能: 1) 分割文件, 生成下载任务; 2) 定时任务: 检索需要下载的任务, 利用多线程下载并限制下载速度; 3) 定时任务: 检索可合并的文件, 把n个文件合并为完整的文件. GitH ...

  4. Java与Go语言差异1 传值还是传引用

    在Java中,复杂类型(除原始类型外的其它类)作为入参,在方法中被修改后,跳出方法对象内的值仍会保持,也就是传的是引用.原始类型传的是值,如int, double等原始类型. Java代码: publ ...

  5. c#---params参数

    写一个方法,求一个同学的总成绩 static void Main(string[] args) { , , }; Test("张三", n); Console.ReadKey(); ...

  6. 无人机通信协议MAVLink简介

    MAVLink MAVLink(Micro Air Vehicle Link,微型空中飞行器链路通讯协议)是无人飞行器与地面站(Ground Control Station ,GCS)之间通讯,以及无 ...

  7. API接口幂等性设计

    目录 幂等性场景 解决方案 幂等性场景 网络延迟导致多次重复提交. 表单重复提交. 解决方案 每次提交都使用一个Token,Token保证临时且唯一即可 token生成规则(单机应用):token+U ...

  8. 限定输入框只能输入数字, TextBox的TextChanged事件调用

    /// <summary> /// 限定输入框只能输入数字, TextBox的TextChanged事件调用 /// </summary> /// <param name ...

  9. 在oracle中使用merge into实现更新和插入数据

    目录 oracle中使用merge into DUAL表解释 使用场景 用法 单表 多表 oracle中使用merge into DUAL表解释 在Oracle数据库中,dual是Oracle中的一个 ...

  10. CSS实现同一行中图片和文本垂直居中

    1.为图片和文本都设置vertical-align:middle