C++容器,迭代器,容器的适配器

版权声明:所有的博客都是作为个人笔记的。。。。。。 https://blog.csdn.net/qq_35976351/article/details/82772787

容器

顺序容器

主要靠下标和迭代器进行操作。顺序性的主要靠下标,链式的靠迭代器访问。

包含了顺序型的容器和链式的容器。

连续型的包括:

  • vector:向量,可以快速扩展和删除元素,在队尾的操作有优势!
  • deque:双端队列,可以快速的从队首和队尾添加或者删除元素,适合双向的操作。
  • array:数组,大小必须是固定的。
  • string:字符串

链式的包括:

  • list:双向链表
  • forward_list:单向链表。没有size操作。

关联容器

简介

主要靠关键字进行访问。关联容器主要是分为两类:set类型的和map类型的。同时也可以按照有序和无序分类,也可以按照顺序保存和无序保存分类。

总共有8中类型额关联容器:

  • map:关键字-值,关键字唯一
  • set:关键字,关键字唯一
  • multimap:关键字-值,关键字可以重复
  • multiset:关键字:关键字可以重复
  • unordered_map:哈希函数组织的map,关键字唯一
  • unordered_set:哈希函数组织的set,关键字唯一
  • unordered_multimap:哈希函数组织的multimap,关键字不唯一
  • unordered_multiset:哈希函数组织的multiset,关键字不唯一

上述以unordered开头的都是无序的关联容器。关联容器都是支持模板类型操作的。

关联容器不支持顺序容器的那种与位置有关的操作,比如push_backpush_front等,这些操作无意义。关联容器的迭代是双向的。

有序关联容器的关键字必须定义比较函数,否则无法放入容器,因为容器需要靠<比较函数才能进行数据结构的构建。使用有序容器存储自定义的数据类型时,除了给出数据类型的模板之外,还要给出自定义的操作类型。不过我们一般都是重载运算符<进行操作。

#include <iostream>
#include <set>
using namespace std;

class Data1{
public:
  Data1(int _a,int _b=0,int _c=0):a(_a),b(_b),c(_c){}
  bool operator<(const Data1 &d){
    return a<d.a;
  }
  int a,b,c;
};

int main(){
  set<Data1>s1;
  return 0;
}

pair数据类型,存在于utility头文件。该数据类型用于map大类的操作,具体有一下几个操作:

  • pair<T1, T2> p;声明一个pair
  • pair<T1, T2> p(v1, v2);声明并初始化
  • pair<T1, T2>p{v1, v2} ;声明并初始化
  • auto p=make_pair(v1,v2);初始化,编译器自动推断所有类型。
  • p.first:返回键值
  • p.second:返回对应的数据
  • p1 < p2:比较的是键值,对于其他的运算符同理。

主要操作

几个特性:

  • key_type:容器的关键字类型
  • mapped_type:每个关键字关联的类型,只适用于map类型
  • value_type:数据类型
    • 对于set类型,这与key_type相同
    • 对于map类型,为pair<const key_value, mapped_type>

上述说的类型是大类。

迭代操作

一般使用自动推导得到迭代器,迭代器是value_type类型的指针。

对于set来说,就是元素模板的类型;对于map来说,是pair<const key_value, mapped_type>类型。

注意,set的迭代器是const类型的,只能读不能修改,这很好理解,因为一旦修改键值,所有的排列顺序都要进行改变;而map的不能修改键值,但是可以修改键值对应的数据:

#include <iostream>
#include <map>
using namespace std;

int main(){
  map<int,string>m{{1,"a"},{2,"b"}};
  for(auto it=m.begin();it!=m.end();it++){
    it->second="new";  // 在这里修改对应的数据
  }
  cout<<m[1]<<endl;   // 输出new
  return 0;
}

对于迭代操作,只要是有序容器,都是按照字典序输出的。

一般来说,关联容器的迭代器都是只读类型的,我们平时用的sort等的算法都不适用这种容器,但是可以使用find的成员给键值定位。也可以使用copy进行复制操作。

插入操作

单个键值的插入:

使用内置的insert成员进行操作。几个常用的操作:

  • c.insert(v),插入对应的valut_type类型的数据
  • c.emplace(args),对于单键值的关联容器,只有元素不存在的时候进行插入,多键值的直接插入;返回一个pair类型的数据,第一个是指向含有关键字的元素,第二个是bool,表示是否插入成功。
  • c.insert(b,e)b和e是迭代器,表示插入一个区间的value_type类型数据
  • c.insert(il)表示使用花括号的类型

上述的返回值都是一个pair,作用同上。

多个键值的插入:

不返回任何数据,因为总是可以进行数据插入。

删除操作

使用erase进行删除操作:

  • c.erase(k):删除关键字为k的元素,返回删除的个数,不存在返回0
  • c.erase(p):删除迭代器指向的元素,必须是内部的,不能是end()
  • c.erase(b,e):删除区间内的元素

map的下标操作

下标操作只适用于mapunordered_map,不能用于多键值的;同时,元素必须是非const类型的。set没有下标操作。两种数据类型:

  • c[k]:返回关键字是k的元素,如果k不存在,进添加这个关键字,并初始化
  • c.at(k):返回关键为k的元素,不存在抛出out_of_range异常

上述返回的都是mapped_type类型的数据。

访问操作

  • c.find(k):返回第一个迭代器,指向关键字是k的,不存在返回end()
  • c.count(k):返回关键字为k的个数
  • c.lower_bound(k):返回第一个迭代器,指向第一个关键字不小于k元素,不适于无序容器!
  • c.upper_bound(k):返回第一个迭代器,指向第一个关键字大于k的元素,不适于无序容器!
  • c.equal_range(k):返回一个pair,表示范围。如果不存在,成员均为end()

无序容器

无序容器使用hash函数和==进行运算。主要处理元素没有明显有序关系的情况,理论上有更好的性能,但是实际上需要多次进行工程验证效率。

无序容器的插入删除和有序容器基本一致,只是没有和序列有关的操作。无序容器使用了一个链式的桶状结构,性能取决于哈希函数的质量和桶的数量与大小。如果是自定义的数据类型,我们需要自己来说明哈希函数,否则不能直接使用。

#include <iostream>
#include <unordered_set>  // 注意头文件
#include <string>
#include <functional>
using namespace std;

class Data{
public:
  Data(int a,string s):n(a),str(s){}
  int n;
  string str;
};

size_t hasher(const Data &d){
  return hash<string>()(d.str); 
}

bool cmp(const Data& d1, const Data &d2){
  return d1.str==d2.str;
}

int main(){
  using SD_multiset=unordered_multiset<Data,
                    decltype(hasher)*, decltype(cmp)*>;
  // 桶的大小,哈希函数,比较函数
  SD_multiset data_set(10,hasher,cmp);
  Data d(1,"hello");
  data_set.insert(d);
  return 0;
}

迭代器

迭代器是一个泛型的指针类型,只要是可迭代的容器,都可以使用迭代器。适配器不可以使用,比如stackqueuepriority_queue等的数据类型。

  • begin()是首元素
  • end()是尾后元素
  • 若容器是空的,则begin()==end()

迭代器的标准运算成员:

  • *iter取内容
  • iter->item取出内部的成员
  • ++iter指向下一个元素
  • --iter指向上一个
  • iter1==iter2判断是否指向同一个元素
  • iter1!=iter2不指向同一个元素的判定

使用了迭代器的循环体,不要向迭代器所属的容器添加元素!!!

非标准迭代器成员的运算符号根据具体的容器确定。

容器的适配器

容器的适配器是一个机制,使得某种事物看起来像另一种事物。给出基本类型和操作:

  • size_type:保证当前类型的最大对象的大小
  • value_type:元素的类型
  • container_type:实现适配器底层容器的类型
  • A a:创建一个A类型的空适配器a
  • A a(c):创建一个名为a的适配器,带有容器c的一个拷贝!!!
  • a.empty():判断是否是空,空返回true,否则是false
  • a.size():返回元素的个数
  • swap(a,b):交换a,b的内容,要求比素食同类型的元素,底层容器类型也必须相同
  • a.swap(b):同上

栈默认使用deque作为底层,队列默认使用dequepriority_queue默认使用vector作为底层。也可以自定义底层容器。需要注意的是:适配器不能进行迭代!!

猜你喜欢

转载自blog.csdn.net/qq_35976351/article/details/82772787