`
zhava
  • 浏览: 5054 次
  • 性别: Icon_minigender_1
  • 来自: 重庆
最近访客 更多访客>>
文章分类
社区版块
存档分类
最新评论

C++ STL学习(2)

阅读更多
map采用红黑树结构进行数据组织,查询速度稳定O(log2N),内存占用较小,效率稳定。
hash_map采用哈希表,查表的速度为最好O(1)最坏O(N)(冲突时)。但是内存使用量大,并且当数据量大的时候存在哈希冲突的问题。

以下内容转自:http://blog.csdn.net/skyremember/article/details/2941076
这篇文章来自我今天碰到的一个问题,一个朋友问我使用map和hash_map的效率问题,虽然我也了解一些,但是我不敢直接告诉朋友,因为我怕我说错了,通过我查询一些帖子,我这里做一个总结!内容分别来自
alvin_lee ,codeproject,codeguru.baidu等等!

先看看alvin_lee 朋友做的解析,我觉得还是很正确的,从算法角度阐述了他们之间的问题!

实际上这个问题不光C++会遇到,其他所有语言的标准容器的实现及选择上都是要考虑的。做应用程序你可能觉得影响不大,但是写算法或者核心代码就要小心了。今天改进代码,顺便又来温习基础功课了。



  还记得Herb Sutter那极有味道的《C++对话系列》么,在其中《产生真正的hash对象》这个故事里就讲了map的选择。顺便回顾一下,也讲一下我在实用中的理解。

   选择map容器,是为了更快的从关键字查找到相关的对象。与使用list这样的线性表容器相比,一可以简化查找的算法,二可以使任意的关键字做索引,并 与目标对象配对,优化查找算法。在C++的STL中map是使用树来做查找算法,这种算法差不多相当与list线性容器的折半查找的效率一样,都是O (log2N),而list就没有map这样易定制和操作了。

  相比hash_map,hash_map使用hash表来排列配 对,hash表是使用关键字来计算表位置。当这个表的大小合适,并且计算算法合适的情况下,hash表的算法复杂度为O(1)的,但是这是理想的情况下 的,如果hash表的关键字计算与表位置存在冲突,那么最坏的复杂度为O(n)。

  那么有了这样的认识,我们应该怎么样选用算法 呢?前两天看Python文章的时候,不知道哪个小子说Python的map比c++的map快,如何如何的。但是他并不知道Python是默认使用的 hash_map,而且这些语言特征本质上是使用c/c++写出来的,问题在与算法和手段,而不是在于语言本身的优劣,你熟悉了各种算法,各种语言的细 节、设计思想,还能在这偏激的嚷嚷孰好孰坏(片面与偏激的看待事物只能表明愚昧与无知,任何事物都有存在的价值,包括技术)。显然C++的STL默认使用 树结构来实现map,是有考究的。

  树查找,在总查找效率上比不上hash表,但是它很稳定,它的算法复杂度不会出现波动。在一次 查找中,你可以断定它最坏的情况下其复杂度不会超过O(log2N)。而hash表就不一样,是O(1),还是O(N),或者在其之间,你并不能把握。假 若你在开发一个供外部调用的接口,其内部有关键字的查找,但是这个接口调用并不频繁,你是会希望其调用速度快、但不稳定呢,还是希望其调用时间平均、且稳 定呢。反之假若你的程序需要查找一个关键字,这个操作非常频繁,你希望这些操作在总体上的时间较短,那么hash表查询在总时间上会比其他要短,平均操作 时间也会短。这里就需要权衡了。

  这里总结一下,选用map还是hash_map,关键是看关键字查询操作次数,以及你所需要保证 的是查询总体时间还是单个查询的时间。如果是要很多次操作,要求其整体效率,那么使用hash_map,平均处理时间短。如果是少数次的操作,使用 hash_map可能造成不确定的O(N),那么使用平均处理时间相对较慢、单次处理时间恒定的map,考虑整体稳定性应该要高于整体效率,因为前提在操 作次数较少。如果在一次流程中,使用hash_map的少数操作产生一个最坏情况O(N),那么hash_map的优势也因此丧尽了。

下面先看一段代码,从Codeproject的 Jay Kint:

// familiar month example used
// mandatory contrived example to show a simple point
// compiled using MinGW gcc 3.2.3 with gcc -c -o file.o
// file.cpp

#include <string>
#include <ext/hash_map>
#include <iostream>

using namespace std;
// some STL implementations do not put hash_map in std
using namespace __gnu_cxx;

hash_map<const char*, int> days_in_month;

class MyClass {
   static int totalDaysInYear;
public:
   void add_days( int days ) { totalDaysInYear += days; }
   static void printTotalDaysInYear(void)
   {
       cout << "Total Days in a year are "
           << totalDaysInYear << endl;
   }
};

int MyClass::totalDaysInYear = 0;

int main(void)
{
   days_in_month["january"] = 31;
   days_in_month["february"] = 28;
   days_in_month["march"] = 31;
   days_in_month["april"] = 30;
   days_in_month["may"] = 31;
   days_in_month["june"] = 30;
   days_in_month["july"] = 31;
   days_in_month["august"] = 31;
   days_in_month["september"] = 30;
   days_in_month["october"] = 31;
   days_in_month["november"] = 30;
   days_in_month["december"] = 31;

   // ERROR: This line doesn't compile.
   accumulate( days_in_month.begin(), days_in_month.end(),
       mem_fun( &MyClass::add_days ));

   MyClass::printTotalDaysInYear();

   return 0;
}


当然上面的代码完全可以使用STL来实现:

引用

Standard C++ Solutions
The Standard C++ Library defines certain function adaptors, select1st, select2nd and compose1, that can be used to call a single parameter function with either the key or the data element of a pair associative container.

select1st and select2nd do pretty much what their respective names say they do. They return either the first or second parameter from a pair.

compose1 allows the use of functional composition, such that the return value of one function can be used as the argument to another. compose1(f,g) is the same as f(g(x)).

Using these function adaptors, we can use for_each to call our function.

hash_map my_map;
for_each( my_map.begin(), my_map.end(),
         compose1( mem_fun( &MyType::do_something ),
                   select2nd                    MyType>::value_type>()));
Certainly, this is much better than having to define helper functions for each pair, but it still seems a bit cumbersome, especially when compared with the clarity that a comparable for loop has.

for( hash_map::iterator i =
        my_map.begin();
    i != my_map.end(), ++i ) {

    i->second.do_something();
}
Considering it was avoiding the for loop for clarity's sake that inspired the use of the STL algorithms in the first place, it doesn't help the case of algorithms vs. hand written loops that the for loop is more clear and concise.

with_data and with_key
with_data and with_key are function adaptors that strive for clarity while allowing the easy use of the STL algorithms with pair associative containers. They have been parameterized much the same way mem_fun has been. This is not exactly rocket science, but it is quickly easy to see that they are much cleaner than the standard function adaptor expansion using compose1 and select2nd.

Using with_data and with_key, any function can be called and will use the data_type or key_type as the function's argument respectively. This allows hash_map, map, and any other pair associative containers in the STL to be used easily with the standard algorithms. It is even possible to use it with other function adaptors, such as mem_fun.

hash_map my_vert_buffers;

void ReleaseBuffers(void)
{
   // release the vertex buffers created so far.
   std::for_each( my_vert_buffers.begin(),
       my_vert_buffers.end(),
       with_data( boost::mem_fn(
           &IDirect3DVertexBuffer9::Release )));
}
Here boost::mem_fn is used instead of mem_fun since it recognizes the __stdcall methods used by COM, if the BOOST_MEM_FN_ENABLE_STDCALL macro is defined.


另外添加一些实战的例子:
连接是:
http://blog.sina.com.cn/u/4755b4ee010004hm

摘录如下:

引用

一直都用的STL的map,直到最近库里数据量急剧增大,听别的做检索的同学说到hash_map,一直都打算换回来,今天好好做了个实验测试了哈hash_map的功能,效果以及与map比较的性能.
     首先,要说的是这两种数据结构的都提供了KEY-VALUE的存储和查找的功能.但是实现是不一样的,map是用的红黑树,查询时间复杂度为log (n),而hash_map是用的哈希表.查询时间复杂度理论上可以是常数,但是消耗内存大,是一种以存储换时间的方法.
     就应用来说,map已经是STL标准库的东西,可是hash_map暂时还未进入标准库,但也是非常常用也非常重要的库.
     这次所做的测试是对于100W及的文件列表,去重的表现,即是对文件名string,做map!
用到的头文件:


#include <time.h>    //计算时间性能用
     #include <ext/hash_map>   //包含hash_map 的头文件
     #include <map>             //stl的map
     using namespace std;        //std 命名空间
     using namespace __gnu_cxx;    //而hash_map是在__gnu_cxx的命名空间里的

//测试3个环节:用map的效率,hash_map系统hash函数的效率及自写hash函数的效率.

    11 struct str_hash{      //自写hash函数
    12     size_t operator()(const string& str) const
    13     {
    14         unsigned long __h = 0;
    15         for (size_t i = 0 ; i < str.size() ; i ++)
    16         {
    17             __h = 107*__h + str[i];
    18         }
    19         return size_t(__h);
    20     }
    21 };

    23 //struct str_hash{    //自带的string hash函数
    24 //        size_t operator()(const string& str) const
    25 //      {
    26 //          return __stl_hash_string(str.c_str());
    27 //      }
    28 //};

    30 struct str_equal{      //string 判断相等函数
    31     bool operator()(const string& s1,const string& s2) const
    32     {
    33         return s1==s2;
    34     }
    35 };

//用的时候
    37 int main(void)
    38 {
    39     vector<string> filtered_list;
    40     hash_map<string,int,str_hash,str_equal> file_map;
    41     map<string,int> file2_map;
    42     ifstream in("/dev/shm/list");
    43     time_t now1 = time(NULL);
    44     struct tm * curtime;
    45     curtime = localtime ( &now1 );
    46     cout<<now1<<endl;
    47     char ctemp[20];
    48     strftime(ctemp, 20, "%Y-%m-%d %H:%M:%S" , curtime);
    49     cout<<ctemp<<endl;
    50     string temp;
    51     int i=0;
    52     if(!in)
    53     {
    54         cout<<"open failed!~"<<endl;
    55     }
    56     while(in>>temp)
    57     {
    58         string sub=temp.substr(0,65);
    59         if(file_map.find(sub)==file_map.end())
    60 //      if(file2_map.find(sub)==file2_map.end())
    61         {
    62             file_map[sub]=i;
    63 //          file2_map[sub]=i;
    64             filtered_list.push_back(temp);
    65             i++;
    66 //          cout<<sub<<endl;
    67         }
    68     }
    69     in.close();
    70     cout<<"the total unique file number is:"<<i<<endl;
    71     ofstream out("./file_list");
    72     if(!out)
    73     {
    74         cout<<"failed open"<<endl;
    75     }
    76     for(int j=0;j<filtered_list.size();j++)
    77     {
    78         out<<filtered_list[j]<<endl;
    79     }
    80     time_t now2=time(NULL);
    81     cout<<now2<<endl;
    82     curtime = localtime ( &now2 );
    83     strftime(ctemp, 20, "%Y-%m-%d %H:%M:%S" , curtime);
    84     cout<<now2-now1<<"/t"<<ctemp<<endl;
    85     return 0;
    86 }


引用

得出来的结论是:(文件list有106W,去重后有51W)
   1.map完成去重耗时34秒
   2.hash_map用系统自带的函数,耗时22秒
   3.hash_map用自己写的函数,耗时14秒
      测试结果充分说明了hash_map比map的优势,另外,不同的hash函数对性能的提升也是不同的,上述hash函数为一同学,测试N多数据后得出的经验函数.
可以预见,当数量级越大时越能体现出hash_map的优势来!~


当然最后作者的结论是错误的,hash_map的原理理解错误!从第一个朋友的回答就可以体会到这个问题!

最后对于C++Builder用户,应该通过以下方法添加:
#include "stlport/hash_map"
才可以正确的使用hash_map
分享到:
评论

相关推荐

    C++ STL学习资料

    在C++ STL的学习中,以下几个核心概念至关重要: 1. 容器:STL的核心组成部分之一,它们是数据结构的模板类,如vector(动态数组)、list(双向链表)、deque(双端队列)、set(集合)、map(映射)和unordered_...

    C++&STL学习资料

    "C++STL.pdf"和"C++_标准模板库(STL).pdf"可能提供了更详细的STL理论知识,涵盖了STL的设计理念、内部工作原理以及一些高级特性的使用,比如迭代器的深层理解、STL中的模板元编程和自定义容器及算法的实现。...

    stl几个用法,c++stl 学习

    stl几个(set map vector string)用法,c++ stl 学习资料

    C++ STL教程pdf

    通过深入学习C++ STL,开发者可以编写出更加高效、可维护的代码,同时也可以更好地理解和利用C++的强大功能。在实际开发中,熟练掌握STL能显著提高编程效率,并有助于写出符合现代编程实践的高质量代码。

    C++STL学习笔记.pdf

    C++ Standard Template Library(STL)是C++编程语言的一个重要组件,它提供了一系列通用的数据结构和算法模板,使程序员能够以一种标准化和高效的方式处理数据。STL的主要组成部分包括容器(containers)、迭代器...

    stl的学习:c++ STL

    学习STL时,你需要理解每个组件的工作原理,熟练使用迭代器,掌握常见算法的应用,并学会如何根据问题选择合适的容器和函数对象。通过阅读《STL Programmer's Guide》这样的文档,你可以深入理解STL的内部机制,提升...

    C/C++ STL参考手册 STL帮助文档 中文/英文版都有

    STL,全称为Standard Template ...通过阅读这两份文档,读者可以深入学习如何利用STL有效地组织和操作数据,提升C++编程的效率。无论是先看中文再对照英文,还是反过来,都能在对比中加深对STL的理解,提高编程技能。

    C++ STL--数据结构与算法实现(余文溪)示例程序代码.rar

    C++ STL,全称为Standard Template Library...学习C++ STL可以帮助开发者提高代码的可读性、可维护性和效率。通过理解和实践余文溪的示例,你可以更好地掌握STL的精髓,从而在实际开发中更有效地利用这个强大的工具。

    C++ STL标准程序库开发指南 源代码.rar

    这个压缩包“C++ STL标准程序库开发指南 源代码.rar”包含了C++ STL的源代码,对于学习和理解STL的内部实现机制非常有帮助。下面我们将深入探讨STL的关键组件、它们的工作原理以及如何通过源代码进行学习。 1. **...

    C++ STL源码剖析(侯捷版本)_C语言_pdf_数据结构_

    《C++ STL源码剖析(侯捷版本)》是一本深度解析C++ ...总的来说,《C++ STL源码剖析(侯捷版本)》是深入学习C++ STL的宝贵资源,通过阅读此书,你将能够更好地理解和利用这个强大的工具,提升你的C++编程水平。

    c++ STL中文版

    在“C++ STL中文版”这本书中,作者深入浅出地讲解了STL的基本概念、使用方法和实际应用,是学习C++ STL的宝贵资源。 STL主要由以下四个核心组件构成: 1. 容器:STL提供了一组预先定义好的容器类,如vector(动态...

    较好的C++ STL 学习资料

    STL学习材料,熟悉C++必备途径,推荐给大家啊

    C++高手之STL学习资料

    STL的学习不仅仅是掌握每个组件的基本用法,更重要的是理解其背后的泛型编程思想和设计模式,这将有助于写出更加高效、可维护的C++代码。对于C++程序员来说,熟练掌握STL是成为高手的重要一步,因为STL的使用能显著...

    C++STL Source.rar

    这个"C++STL Source.rar"文件很可能包含了C++ STL的源代码,对于深入理解STL的工作原理和实现细节非常有帮助。 STL的核心组成部分包括: 1. 容器(Containers):这是STL的基础,提供了数据结构来存储和管理元素...

    深入C++STL中文版

    总的来说,“深入C++STL中文版”这本书籍或资源提供了深入浅出的学习路径,适合希望深入了解C++ STL的读者。通过系统地学习容器、迭代器、算法和函数对象等核心概念,读者能够更高效地利用STL解决实际编程中的问题,...

    stl 电子书两本(C++ STL使用教程,STL编程)

    学习STL不仅可以提升C++编程的效率,也有助于理解和适应现代C++的标准库。通过深入阅读这两本书,程序员可以更好地理解STL的精髓,提高代码质量和可维护性,从而在软件开发领域取得更大的成就。

    C++ STL库源代码(SGI版本)

    STL(Standard Template Library,标准模板库)是C++编程语言中的一个重要组成部分,它提供了一系列高效、可重用的数据结构和算法。在这个“C++ STL库源代码(SGI版本)”中,我们主要关注的是SGI(Stanford Graphics ...

    C++库函数及STL算法

    在C++编程中,库函数和STL(Standard Template Library,标准模板库)是不可或缺的组成部分,它们极大地提升了代码的效率和可读性。本文将深入探讨C++库函数和STL算法,以及如何在实际编程中应用这些概念。 首先,...

    c++ -- stl 学习笔记

    STL(Standard Template Library,标准模板库)是C++编程语言中的一个重要组成部分,它提供了...通过深入学习SGI STL的源码和隐式类型转换,开发者能够更深入地理解STL的工作原理,从而编写出更加高效和可靠的C++程序。

Global site tag (gtag.js) - Google Analytics