在GCC和Visual Studio中使用hash_map

熟悉STL或熟悉ACM/ICPC的话,其中的set, map, multiset, multimap一定用过无数次了,它们都是用平衡二叉树(红黑树)实现的,复杂度为O(lgn)。我们也知道set, map可以通过哈希来实现,复杂度只有O(1),可惜直到现在,unsorted_set或hash_map都没能成为C++标准的一部分(C++0x,- -b)。不过无论在GNU GCC中还是Microsoft Visual Studio中都有对hash_set, hash_map, hash_multiset, hash_multimap的支持。

GCC中的hash_map定义在<ext/hash_map>文件,namespace __gnu_cxx中。要定义一个hash_map<int, int>非常简单:

#include <ext/hash_map>
using namespace __gnu_cxx;
hash_map<intint> hm;

在使用map时,如果我们想要改变元素顺序,或以自定义的struct/class作为key的时候,可以设定map第三个模板参数(默认是less<Key>,即operator<)。对于hash_map,我们需要设定其第三个(hash<Key>)和第四个模板参数(equal_to<Key>, operator==)。

typedef long long my_type;
typedef int any_type;
struct my_hash {
    size_t operator()(const my_type& key) const {
        return (key >> 32) ^ key;
    }
};
struct my_equal_to {
    bool operator()(const my_type& lhs, const my_type& rhs) const {
        return lhs == rhs;
    }
};
hash_map<my_type, any_type, my_hash, my_equal_to> my_hash_map;

对与int等基本类型,系统提供有hash<int>等版本的模板特化,所以只需要指定前两个模板参数就足够了。实现了模板特化的有以下类型

[constchar*, crope, wrope, [signed|unsigned] char, [unsigned] short, [unsigned] int, [unsigned] long

如果需要的话,我们也可以为其他类型实现模板特化

1 // hash_map<Key, Tp, HashFn=hash<Key>, EqualKey=equal_to<Key>, Alloc=allocator<Tp> >
2 #include <cstdio>
3 #include <utility>
4 #include <hash_map>
5 using namespace std;
6 using namespace __gnu_cxx;
7  
8 namespace __gnu_cxx {
9     template<>
10     struct hash<pair<intint> > {
11         size_t operator()(const pair<intint>& key) const {
12             return key.first * key.second;
13         }
14     };
15 }
16 hash_map<pair<intint>, int> hm;

Visual C++的hash_map定义在<hash_map>文件,namespace stdext中,早先在namespace std中。其实现与GCC的不同,模板参数也不一样,比如上面的例子在VC++版本如下

1 // hash_map<Key, Type, Traits=hash_compare<Key, less<Key> >, Allocator=allocator<pair<const Key, Type> > >
2 >
3 class hash_map
4 #include <cstdio>
5 #include <utility>
6 #include <hash_map>
7 using namespace std;
8 using namespace stdext;
9  
10 template<>
11 struct hash_compare<pair<intint> > {
12     // the mean number of elements per bucket
13     static const size_t bucket_size = 4;
14     // the minimum number of buckets
15     static const size_t min_buckets = 8;
16     // hash function
17     size_t operator()(const pair<intint>& key) const {
18         return key.first * key.second;
19     }
20     // compare function
21     bool operator()(const pair<intint>& lhs, const pair<intint>& rhs) const {
22         return lhs < rhs;
23     }
24 };
25 hash_map<pair<intint>, int> hm;

相比前面的hash,上面的hash_compare显然要复杂不少。

不过二者提供的方法基本一致,也和std::map和其他STL容器相似。所以对于上面定义的hash_map,我们都可以用下面的代码进行测试

1 ...
2 int main() {
3     int n;
4     scanf("%d", &n);
5     for (int i = 0; i < n; ++i) {
6         hm[make_pair(i, i)] = i;
7     }
8     for (hash_map<pair<intint>, int>::iterator i = hm.begin(); i != hm.end(); ++i) {
9         printf("%d ", i->second);
10     }
11     printf("\n%d / %d\n", hm.size(), hm.bucket_count());
12     return 0;
13 }

n取12时,GCC 4.4.1得到的结果是

0 1 2 3 4 5 6 7 8 9 10 11
12 / 193

而Visual Studio 2010得到的结果是

0 4 8 1 3 5 7 9 11 2 6 10
12 / 8

由此我们可以看出二者在hash_map实现上的不同。__gnu_cxx::hash_map保持size<=bucket_count,而以193, 389, 769, 1543…这样近似成倍增长的质数作为bucket_count。stdext::hash_map则保持size<=bucket_size*bucket_count,bucket_count起初为min_buckets=8,不足时以8倍增长。

之所以我们要选择hash_map,是为了获得更高的效率。hash_map比map到底快多少呢,我们通过下面的程序来测试一下。通过__GNUC___MSC_VER这两个宏,我们可以把两个版本的代码写到一起

1 #include <map>
2 #include <ctime>
3 #include <cstdio>
4 using namespace std;
5  
6 #if defined(_MSC_VER)
7 # include <hash_map>
8 using stdext::hash_map;
9 #elif defined(__GNUC__)
10 # include <ext/hash_map>
11 using __gnu_cxx::hash_map;
12 #else
13 # error 悲剧啊
14 #endif
15  
16 int main() {
17     clock_t S;
18  
19     S = clock();
20     for (int i = 0; i < 20; ++i) {
21         hash_map<intint> H;
22         for (int i = 0; i < (1 << 20); ++i) {
23             H[i] = i;
24         }
25     }
26     printf("hash_map: %.2lfs\n", (double)(clock() - S) / CLOCKS_PER_SEC);
27  
28     S = clock();
29     for (int i = 0; i < 20; ++i) {
30         map<intint> M;
31         for (int i = 0; i < (1 << 20); ++i) {
32             M[i] = i;
33         }
34     }
35     printf("map: %.2lfs\n", (double)(clock() - S) / CLOCKS_PER_SEC);
36  
37     return 0;
38 }

结果得到的是(gcc和VS的结果分别来自不同机器,因此没有可比性)

* g++ -O2
hash_map: 5.39s
map: 12.35s
* VS2010 Release
hash_map: 9.53s
map: 9.44s

发现g++里hash_map确实要比map快不少,而Visual Studio 2010就是个悲剧,信hash_map不如信春哥啊。

嘛,hash_map确实可能带来一些performance,但不那么stable,所以我们可以考虑优先使用hash_map,而将map最为fallback备胎

1 #define USE_HASH_MAP ?
2 #if USE_HASH_MAP
3 #include <ext/hash_map>
4 typedef __gnu_cxx::hash_map<intint> Hash;
5 #else
6 #include <map>
7 typedef std::map<intint> Hash;
8 #endif

不过在浙大校赛这种judge是优秀的gcc,而比赛环境是混乱的VC6的比赛里。hash_map什么的还是能不用就不用吧……

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

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

相关文章

C++(21)--Astah uml 画C++类图

Astah uml 画C类图1.安装2.使用《老九学堂C课程》《老九学堂C课程》详情请到B站搜索《老九零基础学编程C入门》-------------简单的事情重复做&#xff0c;重复的事情用心做&#xff0c;用心的事情坚持做(老九君)--------------- ASTAH&#xff1a;类图工具&#xff0c;用于理…

redis3.0.0 集群安装详细步骤

Redis集群部署文档(centos6系统) &#xff08;要让集群正常工作至少需要3个主节点&#xff0c;在这里我们要创建6个redis节点&#xff0c;其中三个为主节点&#xff0c;三个为从节点&#xff0c;对应的redis节点的ip和端口对应关系如下&#xff09; 127.0.0.1:7000 127.0.0.1:7…

Redis集群添加节点

Redis集群添加节点 1&#xff1a;首先把需要添加的节点启动 cd /usr/local/cluster/ mkdir 7006 cp /usr/local/cluster/redis.conf /usr/local/cluster/7006/ cd /usr/local/cluster/7006/ vi redis.conf ##修改redis.conf中的port参数的值为7006 redis-server redis.c…

PRML(2)--绪论(下)模型选择、纬度灾难、决策论、信息论

PRML绪论1.3 模型选择1.4 纬度灾难1.5 决策论1.5.1最小错误分率1.5.2最小化期望损失1.5.3拒绝选项1.5.4推断和决策1.5.5 回归问题的损失函数1.6 信息论1.3 模型选择 模型过复杂会造成过拟合问题&#xff0c;需要通过一些技术来降低模型的复杂度。 就最大似然而言&#xff0c;可…

leetcode112 路径总和

给定一个二叉树和一个目标和&#xff0c;判断该树中是否存在根节点到叶子节点的路径&#xff0c;这条路径上所有节点值相加等于目标和。 说明: 叶子节点是指没有子节点的节点。 示例: 给定如下二叉树&#xff0c;以及目标和 sum 22&#xff0c; 5 / \ …

关于游戏架构设计的一些整理吧

一个大型的网落游戏服务器应该包含几个模块:网络通讯,业务逻辑,数据存储,守护监控(不是必须),其中业务逻辑可能根据具体需要,又划分为好几个子模块。 这里说的模块可以指一个进程,或者一个线程方式存在,本质上就是一些类的封装。

linux时间轮 Timing-Wheel的实现

过一段时间上传更新自己的心得&#xff0c;以及linux的时间轮实现 现在git上传自己的C代码 gitgithub.com:pbymw8iwm/Timing-Wheel.git

leetcode128 最长连续序列

给定一个未排序的整数数组&#xff0c;找出最长连续序列的长度。 要求算法的时间复杂度为 O(n)。 示例: 输入: [100, 4, 200, 1, 3, 2] 输出: 4 解释: 最长连续序列是 [1, 2, 3, 4]。它的长度为4 思路&#xff1a;map记录某个连续序列端点的最大长度。 对于数字i&#xff…

C++(22)--继承和派生

继承和派生1.基本概念2.实现公有继承3.私有继承的例子4. 继承和组合《老九学堂C课程》《C primer》学习笔记。《老九学堂C课程》详情请到B站搜索《老九零基础学编程C入门》-------------简单的事情重复做&#xff0c;重复的事情用心做&#xff0c;用心的事情坚持做(老九君)----…

Python- 解决PIP下载安装速度慢

对于Python开发用户来讲&#xff0c;PIP安装软件包是家常便饭。但国外的源下载速度实在太慢&#xff0c;浪费时间。而且经常出现下载后安装出错问题。所以把PIP安装源替换成国内镜像&#xff0c;可以大幅提升下载速度&#xff0c;还可以提高安装成功率。 国内源&#xff1a; …

leetcode102 二叉树的层次遍历

给定一个二叉树&#xff0c;返回其按层次遍历的节点值。 &#xff08;即逐层地&#xff0c;从左到右访问所有节点&#xff09;。 例如: 给定二叉树: [3,9,20,null,null,15,7], 3 / \ 9 20 / \ 15 7 返回其层次遍历结果&#xff1a; [ [3], [9,20], [15…

Windows Git客户端搭建

最近开始做Windows 开发&#xff0c;所以找了一些windows下安装git的教程 本文环境&#xff1a; 操作系统&#xff1a;Windows XP SP3 Git客户端&#xff1a;TortoiseGit-1.8.16.0-32bit 一、安装Git客户端 全部安装均采用默认&#xff01; 1. 安装支撑软件 msysgit: http://ms…

C++(23)--多态性与虚函数

多态性与虚函数1.静态多态-重载2.动态多态-重写2.1 向上转换/向下转换3.虚函数的工作原理4.纯虚函数和抽象类5.补充项目(都市浮生记)-卒《老九学堂C课程》学习笔记。《老九学堂C课程》详情请到B站搜索《老九零基础学编程C入门》-------------简单的事情重复做&#xff0c;重复的…

如何在Appscale下发布自己的应用(一)

本篇文章主要讲如何在本地搭建appscale环境。由于国内的信息资源有限&#xff0c;很多重要的论坛被墙了&#xff0c;所以遇到不少麻烦&#xff0c;由于最近一段时间vpn也被封掉了&#xff0c;我只能通过特殊渠道方法来翻墙查阅资料&#xff0c;走了不少弯路。 1.先说系统和环境…

总结了线程安全性的二十四个精华问题

1、对象的状态&#xff1a;对象的状态是指存储在状态变量中的数据&#xff0c;对象的状态可能包括其他依赖对象的域。在对象的状态中包含了任何可能影响其外部可见行为的数据。 2、一个对象是否是线程安全的&#xff0c;取决于它是否被多个线程访问。这指的是在程序中访问对象的…

如何在Appscale下发布自己的应用(二)

本文开始讲如何发布自己的app应用到appscle上 建好appscle网站后&#xff0c;可以在命令行通过 appscle deploy apppathname 来发布自己应用。 除了用命令行提交应用之外&#xff0c;还可以通过appscale的网站直接提交&#xff0c;选择 upload application->选择上传文件-&g…

Python模块(7)-SciPy 简易使用教程

SciPy 简易使用教程1. 符号计算2. 函数向量化3. 波形处理scipy.signal3.1 滤波器3.2 波峰定位基于numpy的一个高级模块&#xff0c;为数学&#xff0c;物理&#xff0c;工程等方面的科学计算提供无可替代的支持。 做重要的思想是&#xff1a;符号计算和函数向量化 1. 符号计算…

Xcode的Architectures和Valid Architectures的区别

目录[-] Xcode的Architectures和Valid Architectures的区别 Architectures Valid Architectures 原因解释如下&#xff1a; 参考1&#xff1a; 所有IOS设备详情列表 List of iOS devices - Wikipedia, the free encyclopedia 参考2&#xff1a; iOS 7: 如何为iPhone 5S编译64位…

Python模块(8)-sklearn 简易使用教程

sklearn 简易使用教程1.scikit-learn的数据集2.scikit-learn 的训练和预测scikit-learn 是在Numpy,SciPy,Matplotlib三个模块上编写的&#xff0c;数据挖掘和数据分析的一个简单有效的工具。scikit-learn包括6大功能&#xff1a;分类&#xff0c;回归&#xff0c;聚类&#xff…

如何发布GAE的应用(一)

安装googleSDK的环境&#xff1a; 1 下载安装包从官网下载 https://cloud.google.com/sdk/downloads -> https://dl.google.com/dl/cloudsdk/channels/rapid/downloads/google-cloud-sdk-170.0.0-windows-x86_64-bundled-python.zip 2 如果本地安装了python&#xff0c;直…