zoukankan      html  css  js  c++  java
  • 几种常见容器比较和分析 hashmap, map, vector, list ...hash table

    list支持快速的插入和删除,但是查找费时;
    vector支持快速的查找,但是插入费时。
    map查找的时间复杂度是对数的,这几乎是最快的,hash也是对数的。
    如果我自己写,我也会用二叉检索树,它在大部分情况下可以保证对数复杂度,最坏情况是常数复杂度,而std::map在任何情况下都可以保证对数复杂度,原因是它保证存诸结构是完全二叉检索树,但这会在存诸上牺牲一些时间。
    STL 中的map内部是平衡二叉树,所以平衡二叉树的性质都具备。查找数据的时间也是对数时间。 vector,在分配内存上一般要比   new   高效的多。
    为什么说hash_map是对数级的?在不碰撞的情况下,hash_map是所有数据结构中查找最快的,它是常数级的。
    如果对问题设计了足够好的hash算法,保证碰撞率很低,hash_map的查找效率无可置疑。
    另外,STL的map,它的查找是对数级的,是除hash_map外最高的了,你可以说“也许还有改进余地”,但对于99.9999%的程序员,设计一个比STL   map好的map,我执悲观态度。
    STL的map有平衡策略(比如红黑树什么的),所以不会退化,不需要考虑数据本身的分布问题。只不过,如果数据本身是排好序的,用vector或heap会明显的快些,因为它们的访问比较简单。
    我想没必要怀疑stl::map的查找效率,影响效率最主要的因素是什么?算法,在查找问题上,有什么算法比RB_tree更好吗?至少现在还没有。不否 认你可以通过自己写代码,设计一个符合你需要的BR—TREE,比stl::map简捷那么一点,但最多也就每次迭代中少一行指令而已,处理十万个数据多 执行十万行指令,这对你重要吗?如果你不是在设计OS像LINUX,没人会关注这十万行指令花的时间。
    rb-tree的时间花在了插入和删除上,如果你不是对插入和删除效率要求很高,你没有理由不选择基于rb-tree的stl::map。
    大多数程序员写不出比std::map更好的map,这是当然的。然而并不是std::map的所有特性都出现在我们的程序中,自己编写的可以更适合自己的程序,的确会比std::map更快一些。
    关于hash_map,它与map的实现机制是不一样的,map内部一般用树来实现,其查找操作是O(logN)的,这个没有争议,我就不多说了。
    hash_map的查找,内部是通过一个从key到value的运算函数来实现的,这个函数“只接受key作为参数”,也就是说,hash_map的查找 算法与数据量无关,所以认为它是O(1)级的。来这里的应该都是达人,可以参看《数据结构》。当然,事实总不这样完美,再引一段前面我自已说的话,进一步 说明,以免误会:
    -----------------------------------------
    在不碰撞的情况下,hash_map是所有数据结构中查找最快的,它是常数级的。
    ------------------------------------------
    注意我的前提:“在不碰撞的情况下”,其实换句话说,就是要有足够好的hash函数,它要能使key到value的映射足够均匀,否则,在最坏的情况下,它的计算量就退化到O(N)级,变成和链表一样。
    如果说   hash_map   是所有容器中最慢的,也只能说:“最拙劣的hash函数”会使hash_map成为查找最慢的容器。但这样说意义不大,因为,最凑巧的排列能使冒泡排序成为最快的排序算法。
    BS: "对于大型容器而言,hash_map能够提供比map快5至10倍的元素查找速度是很常见的,尤其是在查找速度特别重要的地方.另一方面,如果hash_map选择了病态的散列函数,他也可能比map慢得多. "
    ANSIC++在1998年之后就没再有重大改变,并且决定不再向C++标准库中做任何重大的变更,正是这个原因,hash   table(包括hash_map)并没有被列入标准之中,虽然它理应在C++标准之中占有一席之地。
    虽然,现在的大多数编译平台支持hash   table,但从可移植性方面考虑,还是不用hash   table的好。
    1.有的时候vector可以替代map
    比如key是整数,就可以以key的跨度作为长度来定义vector。
    数据规模很大的时候,差异是惊人的。当然,空间浪费往往也惊人。
    2.hash是很难的东西
    没有高效低碰撞的算法,hash_xxx没有意义。
    而对不同的类型,数据集,不可能有优良的神仙算法。必须因场合而宜。
    俺有的解决方法是GP,可不是饭型,是遗传编程,收效不错。
    你的百万级的数据放到vector不大合适。因为vector需要连续的内存空间,显然在初始化这个容器的时候会花费很大的容量。
    使用map,你想好了要为其建立一个主键吗?如果没有这样的需求,为什么不考虑deque或者list?
    map默认使用的是deque作为容器。其实map不是容器,拿它与容器比较意义不大。因为你可以配置它的底层容器类型。
    如果内存不是考虑的问题。用vector比map好。map每插入一个数据,都要排序一次。所以速度反不及先安插所有元素,再进行排序。
    用 binary_search对已序区间搜索,如果是随机存取iterator,则是对数复杂度。可见,在不考虑内存问题的情况下,vector比map 好。
    如果你需要在数据中间进行插入,list 是最好的选择,vector   的插入效率会让你痛苦得想死。
    涉及到查找的话用map比较好,因为map的内部数据结构用rb-tree实现,而用vector你只能用线性查找,效率很低。
    stl还提供了 hash容器,理论上查找是飞快~~~。做有序插入的话vector是噩梦,map则保证肯定是按key排序的,list要自己做些事情。
    HASH类型的查找肯定快,是映射关系嘛,但是插入和删除却慢,要做移动操作, LIST类型的使链式关系,插入非常快,但是查找却费时,需要遍历~~ , 还是用LIST类型的吧,虽然查找慢点,
    先快速排序,然后二分查找,效率也不低

    vector和list区别
    stl提供了三个最基本的容器:vector,list,deque。

    vector和built-in数组类似,它拥有一段连续的内存空间,并且起始地址不变,因此它能非常好的支持随即存取,即[]操作符,但由于它的内存空间是连续的,所以在中间进行插入和删除会造成内存块的拷贝,另外,当该数组后的内存空间不够时,需要重新申请一块足够大的内存并进行内存的拷贝。这些都大大影响了vector的效率。

    list就是双向链表(根据sgi stl源代码),因此它的内存空间可以是不连续的,通过指针来进行数据的访问,这个特点使得它的随即存取变的非常没有效率,因此它没有提供[]操作符的重载。但由于链表的特点,它可以以很好的效率支持任意地方的删除和插入。

    deque是一个double-ended queue,它的具体实现不太清楚,但知道它具有以下两个特点:它支持[]操作符,也就是支持随即存取,并且和vector的效率相差无几,它支持在两端的操作:push_back,push_front,pop_back,pop_front等,并且在两端操作上与list的效率也差不多。

    因此在实际使用时,如何选择这三个容器中哪一个,应根据你的需要而定,一般应遵循下面的原则:

    如果你需要高效的随即存取,而不在乎插入和删除的效率,使用vector
    如果你需要大量的插入和删除,而不关心随即存取,则应使用list
    如果你需要随即存取,而且关心两端数据的插入和删除,则应使用deque。
    vector为存储的对象分配一块连续的地址空间,因此对vector中的元素随机访问效率很高。在vecotor中插入或者删除某个元素,需要将现有元素进行复制,移动。如果vector中存储的对象很大,或者构造函数复杂,则在对现有元素进行拷贝时开销较大,因为拷贝对象要调用拷贝构造函数。对于简单的小对象,vector的效率优于list。vector在每次扩张容量的时候,将容量扩展2倍,这样对于小对象来说,效率是很高的。

    list中的对象是离散存储的,随机访问某个元素需要遍历list。在list中插入元素,尤其是在首尾插入元素,效率很高,只需要改变元素的指针。

    综上所述:
    vector适用:对象数量变化少,简单对象,随机访问元素频繁;
    list适用:对象数量变化大,对象复杂,插入和删除频繁。
    作者:simulationer
    链接:https://www.jianshu.com/p/b95d8972466a

    我的总结:
    1.根据他们的功能选择
    2.根据他们的效率
    这根据个人喜好了,有时候并不用最求极致的效率,我个人的风格大多数选择使用方便的。

    vector  如果你需要用到交换这个功能 
    c1.swap(c2)将c1和c2交换。

    list    如果你需要用到反转,排序功能
    reverse() 
    反转链表
    c.sort() 将链表排序,默认升序
    c.sort(comp) 自定义回调函数实现自定义排序

  • 相关阅读:
    Ubuntu下录音机程序的使用
    Bash中的数学计算
    Bash中的数学扩展
    Bash的命令替换
    top的用法
    VirtualBox的快照功能
    格式化输出和printf命令
    read命令读取用户输入
    Bash的作业控制
    Codeforces Round #455 (Div. 2)
  • 原文地址:https://www.cnblogs.com/byfei/p/14104439.html
Copyright © 2011-2022 走看看