修正
一辈子这么长,我不会就喜欢你一个人的。
关于map和unordered_map还需要知道的
map 与unordered_map的效率问题
unordered_map的初始化比较耗时,我们都知道map是红黑树,unordered_map是哈希表,造成性能差异的原因在于,红黑树初始化时,节点只需要一个,后续的插入只是插入新的节点,但是哈希表初始化时就不是那么简单了,哈希表初始化时需要申请一个数组,数组的每个元素都指向一条链表,所以初始化时需要申请很多内存,相比于map,的确更耗时。
unorder_map的插入效率随着数量的增加,基本是map的一点多倍以上,查找方面来说,跟map基本可以说差不多,而如果map中如果放string的话,查找效率明显比存int类型这种类型的消耗很多。因此,如果对排序没有什么要求的话,又要求key为string类型的,强烈要求使用unorder_map。
typedef pair<int, int> edgePair;
typedef unordered_map<edgePair, int, pairHash> edgeMap;//这个在频繁查找中有优势
typedef map<edgePair, int> edgeMap; //这个比较省时
使用空间换取时间。
map/unordered_map原理和使用整理
map的内部实现是二叉平衡树(红黑树);hash_map内部是一个hash_table一般是由一个大vector,vector元素节点可挂接链表来解决冲突,来实现,新版的hash_map都是unordered_map了。
1、 map和hash_map
- STL的map底层是用红黑树实现的,查找时间复杂度是log(n);
- STL的hash_map底层是用hash表存储的,查询时间复杂度是O(1);
- 什么时候用map,什么时候用hash_map?
这个要看具体的应用,不一定常数级别的hash_map一定比log(n)级别的map要好,hash_map的hash函数以及解决地址冲突等都要耗时间,而且众所周知hash表是以空间换时间的,因而hash_map的内存消耗肯定要大,一般情况下,如果记录非常大,考虑hash_map,查找效率会高很多,如果要考虑内存消耗,则要谨慎使用hash_map。
map的使用
1 | map<string, string> namemap; |
2、hash_map的使用
下面是SGI STL的hash_map声明。
template <class _Key, class _Tp, class _HashFcn = hash<_Key>,
class _EqualKey = equal_to<_Key>,
class _Alloc = __STL_DEFAULT_ALLOCATOR(_Tp) >
class hash_map
{
...
}
键,值,hash函数,比较函数,alloc(忽略)
hash函数
在SGI STL中,提供了以下hash函数:
struct hash
struct hash
struct hash
struct hash
struct hash
struct hash
struct hash
struct hash
struct hash
struct hash
struct hash
也就是说,如果你的可以key是以上类型中的一种,你都可以使用缺省的hash函数。当然你自己也可以定义自己的hash函数。对于自定义变量,例如对于string,就必须自定义hash函数,可以用char代替。例如:
struct str_hash{
size_t operator()(const string& str) const
{
unsigned long h = 0;
for (size_t i = 0 ; i < str.size() ; i ++)
h = 5h + str[i];
return size_t(h);
}
};
//如果你希望利用系统定义的字符串hash函数,你可以这样写:
struct str_hash{
size_t operator()(const string& str) const
{
return return __stl_hash_string(str.c_str());
}
};
hash_map<int, string> mymap;
//等同于:
hash_map<int, string, hash<int>, equal_to<int> > mymap;
比较函数
在map中的比较函数,需要提供less函数。如果没有提供,缺省的也是less
//本代码可以从SGI STL
//先看看binary_function 函数声明,其实只是定义一些类型而已。
template <class _Arg1, class _Arg2, class _Result>
struct binary_function {
typedef _Arg1 first_argument_type;
typedef _Arg2 second_argument_type;
typedef _Result result_type;
};
//看看equal_to的定义:
template <class _Tp>
struct equal_to : public binary_function<_Tp,_Tp,bool>
{
bool operator()(const _Tp& __x, const _Tp& __y) const { return __x == __y; }
};
如果你使用一个自定义的数据类型,如struct mystruct或者const char*的字符串,如何使用比较函数?使用比较函数,有两种方法。第一种是:重载==操作符,利用equal_to;看看下面的例子:
struct mystruct{
int iID;
int len;
bool operator==(const mystruct & my) const{
return (iID==my.iID) && (len==my.len) ;
}
};
这样,就可以使用equal_to
struct compare_str{
bool operator()(const char* p1, const char*p2) const{
return strcmp(p1,p2)==0;
}
};
hash_map中几个常用函数
1、hash_map(size_type n) 如果讲究效率,这个参数是必须要设置的。n 主要用来设置hash_map 容器中hash桶的个数。桶个数越多,hash函数发生冲突的概率就越小,重新申请内存的概率就越小。n越大,效率越高,但是内存消耗也越大。
2、const_iterator find(const key_type& k) const. 用查找,输入为键值,返回为迭代器。
3、data_type& operator . 这是我最常用的一个函数。因为其特别方便,可像使用数组一样使用。不过需要注意的是,当你使用[key ]操作符时,如果容器中没有key元素,这就相当于自动增加了一个key元素。因此当你只是想知道容器中是否有key元素时,你可以使用find。如果你 希望插入该元素时,你可以直接使用[]操作符。
4、insert 函数。在容器中不包含key值时,insert函数和[]操作符的功能差不多。但是当容器中元素越来越多,每个桶中的元素会增加,为了保证效率, hash_map会自动申请更大的内存,以生成更多的桶。因此在insert以后,以前的iterator有可能是不可用的。
5、erase 函数。在insert的过程中,当每个桶的元素太多时,hash_map可能会自动扩充容器的内存。但在sgi stl中是erase并不自动回收内存。因此你调用erase后,其他元素的iterator还是可用的。
3、示例
1 | -bash-2.05b$ cat my.cpp |
4、pair
pair是一种模板类型,其中包含两个数据值,两个数据的类型可以不同,基本的定义如下:
pair
a;
表示a中有两个类型,第一个元素是int型的,第二个元素是string类型的,如果创建pair的时候没有对其进行初始化,则调用默认构造函数对其初始化。
pair
a(“James”, “Joy”);
对于pair类,由于它只有两个元素,分别名为first和second,因此直接使用普通的点操作符即可访问其成员。
pair<string, string> a("Lily", "Poly");
string name;
name = pair.second;
生成新的pair对象,可以使用make_pair对已存在的两个数据构造一个新的pair类型:
int a = 8;
string m = "James";
pair<int, string> newone;
newone = make_pair(a, m);
5、举栗子
1 |
|