[C ++的STL笔记---有搬运】

1. list  

      list 由双向链表(doubly linked list)实现而成,元素也存放在堆中,具有双链表结构;每个元素都是放在一块内存中,其内存空间可以是不连续的,维护一对前向和后向指针,通过指针来进行数据的访问,因此支持前向/后向遍历。这个特点使得它的随机存取变得非常没有效率,因此它没有提供 [] 操作符的重载。但是由于链表的特点,它可以很有效率的支持任意地方的插入和删除操作。

list 与 vector、deque 的用法基本一致,除了以下几处不同:

  • list 为双向迭代器,故不支持it+=1
  • list 不支持下标访问和at方法访问。

list容器的迭代器不支持加减等数术运算,因为list容器不支持像vector容器那样的快速随机访问,只能依次访问每个元素。

数据结构的区别

vector

  vector与数组类似,拥有一段连续的内存空间,并且起始地址不变。便于随机访问,时间复杂度为O(1),但因为内存空间是连续的,所以在进入插入和删除操作时,会造成内存块的拷贝,时间复杂度为O(n)。

  此外,当数组内存空间不足,会采取扩容,通过重新申请一块更大的内存空间进行内存拷贝。所以对开始和中间进行操作会引起内存重新分配,复杂度较大,而对末尾进行操作(push_back\pop)效率较好,https://blog.csdn.net/Africa_South/article/details/87875863

List

 list总结不错的博客:https://blog.csdn.net/Africa_South/article/details/87897561 

      list底层是由双向链表实现的,因此内存空间不是连续的。根据链表的实现原理,List查询效率较低,时间复杂度为O(n),不支持下标访问,但插入和删除效率较高。只需要在插入的地方更改指针的指向即可,不用移动数据。

 

迭代器支持不同

异:vector中,iterator支持 ”+“、”+=“,”<"等操作。而list中则不支持。

同:vector<int>::iterator和list<int>::iterator都重载了 “++ ”操作。

参考链接https://www.cnblogs.com/linuxAndMcu/p/10260627.html

其他容器链接:https://www.cnblogs.com/skyfsm/p/6934246.html


2. vector

   vector存储是连续的,支持高效的随机访问和在尾端插入和删除,在其他插入删除操作效率较低


3. 比较:

  vector V.S. list V.S. deque:
  a、若需要随机访问操作,则选择vector;
  b、若已经知道需要存储元素的数目, 则选择vector;
  c、若需要随机插入/删除(不仅仅在两端),则选择list;
  d、如果经常进行添加和删除操作并且不经常随机访问的话,使用list;
  e、若既需要随机插入/删除,又需要随机访问,则需要在vector与list间做个折中;

 

4. 容易的erase (搬运)

 

容器按内存分配方式可以分为链表容器和数组容器。

      所谓的链表容器指的是一种表现方式,包括list、slist等这样基于节点的容器(动态分配内存块)和set、map、multiset、multimap等关联容器(平衡树实现),而数组容器指的是在一块连续的内存上保存元素的连续内存容器,比如vector、deque、string等。

     链表容器以list为例,当执行container.erase(it)时,确实第一个满足条件的元素删除了,但这时it指针已经被删除了,它也不指向任何元素了,所以也只能到此为止了,也就是说上面的代码对于链表容器来说只能正确删除第一个满足条件的元素,针对这个问题我们首先想到的就是在删除指针之前,给其做个备份。

将这个临时变量直接建立在erase实现里,这样做更简洁,也显得专业些。

list<int>::iterator it; 
for (it = lt.begin(); it != lt.end(); ) 
{
    if (*it % 2 == 0)
      lt.erase(it++); //这里是关键
    else
      ++it;
}

 list与vector中的erase用法相同,它们的 erase 函数会返回指向下一个元素的迭代器,因此在遍历时,只需要 it = v.erase(it)重新把下一个元素指针赋给it 即可。如示例代码:

list<int>::iterator it; 
for (it = lt.begin(); it != lt.end(); ) {
  if (*it % 2 == 0)
    it = lt.erase(it);//自动返回下一个元素的地址,不用再主动前移指针
  else
    ++it;
}

 map与set中的erase用法相同,它们的 erase 函数返回值是 void,调用 erase 之后,当前迭代器会失效,无法再用于获取下一个迭代器。因此需要 erase 之前就获取指向下一个元素的迭代器。https://blog.csdn.net/zhaojunwuiris/article/details/80647653

map<int, int>::iterator it = m.begin();  
for (; it != m.end();)  
{  
    if (it->fist == 10) {  
        m.erase(it++);  
    }  
    else {  
        ++it;  
    }  
}

 5 vector提高效率方法 https://www.cnblogs.com/simonote/p/9265374.html ;                             

                                         https://blog.csdn.net/qq_37037492/article/details/86568290

                                         https://www.cnblogs.com/chhuach2005/p/3627011.html

    emplace_back 代替pusk_back,滥用push_back是可怕的性能杀手;

    shrink_to_fit() 释放 vector 用内存,erase和clear不能做到;

    先reserve在push_back,

    尽量提前分配好内存;

finally 选择容器类型的法则:https://blog.csdn.net/qq_36770641/article/details/88667720

1.如果程序是随机范围容器内的元素,则选择vector/deque 容器

2.如果程序必须在容器的中间位置插入元素,则选择list

3.如果程序是在容器首尾插入元素,则选择deque

4.如果只需要在读取输入时在容器中间插入元素,然后随机访问,

则考虑在 输入时 将元素 读入list,接着 对list 重新排序

使其适合顺序访问,然后将 排序后的list 容器 复制 到一个 vector容器

 

 

 

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章