Chapter9 顺序容器

1.概述

容器特性
vector变长数组,随机访问,尾部插入和删除
deque双端队列,随机访问,头尾的插入删除
list双向链表,双向顺序访问,任何位置插入删除
forward_list单向链表,单向顺序访问,任何位置插入删除
array定长数组
string与vector相似,用于保存字符

选择容器的依据

  1. vector是较为一般的选择
  2. listforward_list的额外空间开销较大,不适合小元素
  3. 只有vectordequearray能随机访问
  4. 如果开始要中间插入,后面要随机访问,可以考虑先尾端插入再sort
  5. 如果无法sort解决,可以先用list输入,结束后拷贝到vector进行随机访问
  6. 实在不行只能比较vectorlist的相对开销

2.基本操作


2.1 容器共有操作

	forward_list<int> d;
	vector<int> c;
	// list<int> c;
	// deque<int> c;
	// string c;
	// array<int, 10> a; //必须固定大小,但是可以赋值
	auto it1 = c.begin();                         
	//迭代器,++向大方向自增(forward_list不支持--);如果容器是const的,也会返回const迭代器
	auto it2 = c.cbegin();                        //const 迭代器
	auto it3 = c.rbegin();                        //反向迭代器,++向小方向自增
	auto it4 = c.crbegin();                       //反向const迭代器
	auto size = c.size();                         //容器大小,单向链表不支持
	auto diff = c.end() - c.begin();              //迭代器间距离
	auto emtpy = c.empty();                       //是否为空
	auto t = (c.begin() == c.end()) == c.empty(); //等价的两种表述
	for (auto it = c.begin(); it != c.end(); it++); //遍历元素
	for (auto it = c.cbegin(); it != c.cend(); it++); //只读的遍历

2.2 随机访问

	int n;
    vector<int> c;
    c.back();           //返回尾元素的引用
    c.front();          //返回首元素的引用
    c.at(n);            //返回对应下标元素的引用
    auto &i = c.back(); //赋值给引用变量

2.3 初始化和赋值

    vector<int> v1(c);		//用另一个同类对象初始化
    vector<int> v2(10);		//初始化空间大小
    swap(v1, v2); 
    //常数时间交换(array除外),只是交换了控制信息,迭代器指针等不会失效(string除外)
    vector<int> v3(10, 1);	//指定值初始化
    v3.assign(10, 1);		//赋值
    vector<int> v4(d.begin(), d.end()); //用其他容器初始化
    v4.assign(d.begin(), d.end());      //用其他容器赋值,不能用自己迭代器赋值,因为迭代器会失效
    vector<int> v5{10};
    vector<int> v6 = {10};
    vector<int> v7 = c;

3.插入


3.1 push_back()

除了 arrayforward_list 外均支持
插入的是对象的拷贝,除非使用的是右值引用

	vecotr<string> v;
	string word;
	while(cin >> word)
		v.push_back(word);	//拷贝插入
	v.push_back("123");		//右值引用插入

push_front() 同理


3.2 insert()

	vector<int> c;
    deque<int> q;
    q.insert(q.begin(), 1);                //插入到对应迭代器之前
    q.insert(q.end(), 10, 1);              //插入多个元素
    q.insert(q.end(), {1, 1, 1, 1});       //初始化列表插入
    q.insert(q.end(), c.begin(), c.end()); //不能插入自己的迭代器,因为会失效
    auto it = q.end(); 					   //insert 均返回第一个插入元素位置的迭代器
    int n;
    while (cin >> n)
        it = q.insert(it, n);//利用返回值更新迭代器,反复在同一个位置插入

3.3 emplace()

emplacepush不同,是构造而不是拷贝临时元素
直接调用对应类型的构造函数,适合自定义的元素较为复杂的容器
push会创建临时对象然后拷贝,emplace直接调用构造函数,参数与构造函数匹配

	vector<vector<int>> v;	//下面两句等价
    v.push_back({1,1,1});	//内置类型可以push列表初始化,右值引用移动语义
    v.emplace_back(3,1);	//自定义类型最好用emplace

4.删除

删除的过程中迭代器会失效:
deque所有迭代器都会失效
vecetorstring被删除元素后面的迭代器会失效
因此需要及时更新迭代器

4.1 erase()

	deque<int> q;
    q.pop_back();				//返回void
    q.pop_front();				//返回void
    q.erase(q.begin());			//返回后面一个元素的迭代器
    q.erase(q.begin(),q.end());	//返回最后一个元素后面一个元素的迭代器
    q.clear();					//删除所有,返回void
    //删除特定元素
    vector<int> v{1,1,1};
    auto it = v.begin();
    while(it != v.end())
    {
        if(*it == 2)
            it = v.erase(it);//必须更新迭代器
        else
            ++it;
    }

4.2 forward_list

forward_list 比较特殊,因为单向链表中的添加和删除都需要对前一个元素进行修改

	vector<int> v;
    forward_list<int> f;
    f.before_begin();                         //首前迭代器,方便插入删除
    f.erase_after(f.before_begin());          //删除第一个元素
    f.erase_after(f.before_begin(), f.end()); //删除范围
    f.insert_after(f.before_begin(), 1);      //在首部插入元素
    f.insert_after(f.begin(), 1);             //在第二个位置插入元素
    f.insert_after(f.before_begin(), v.begin(), v.end());
    f.emplace_after(f.before_begin(), 1);
    //链表中插入删除元素需要维护两个迭代器
    auto pre = f.before_begin();
    auto cur = f.begin();
    while(cur != f.end())
    {
        if(*cur == 2)
            cur = f.erase_after(pre);
        else
        {
            ++cur;
            ++pre;
        }
    }

5. 改变容器大小

array不支持

	vector<int> v(10);
    v.resize(20);
    v.resize(30,1);//新添加进的元素全部初始化为1

6. 迭代器失效

6.1 插入

容器操作
vector / string如果存储空间重新分配,所有迭代器、指针、引用都会失效
如果空间没有重新分配,插入位置之后的迭代器、指针、引用会失效
deque除首尾位置外任何位置,所有迭代器、指针、引用都会失效
如果在首尾插入,迭代器会失效,指针和引用不会失效(说明地址没有变)
list / forward_list迭代器、指针、引用仍有效

6.2 删除

容器操作
vector / string删除元素之后的均失效
deque除首尾位置外任何位置,所有迭代器、指针、引用都会失效
如果在删除尾元素,尾后迭代器会失效,其他不受影响
如果删除首元素,不受影响
list / forward_list除删除位置外的迭代器、指针、引用仍有效

因此如果用尾后迭代器作循环的判断条件,不能保存该迭代器,必须每次调用end()
下面是典型错误,在循环过程中更新了begin但是没有更新end

	vector<int> v{1,2,3,4,5,6);
	auto begin = v.begin(), end = v.end();
	while(begin != end)
	{
		++begin();					//向前移动,使得插入元素在begin后面
		begin = v.inset(begin(),42);//已失效更新begin迭代器
		++begin();					//跳过刚插入的元素
	}

7.vector内存管理

每次分配内存的时候都会多分配(adaptor)一些内存区域,但是并没有构建(construct)。在push_back()的过程中逐步构建新元素,如果现有的内存空间已满,不够用来构建,就将所有内容一定到一个新分配的更大的内存空间
内存管理函数:

	v.size();			//已经存在的元素的数量
	//适用于vector、string、deque:
	v.shrink_to_fit(n);	//将capacity减少到size大小
	//适用于vector、string:
	v.capacity();		//返回已经分配的内存中可容纳元素的数量
	v.reserve(n);		//分配能够容纳n个元素的内存空间	

已分配空间用完时,标准库的实现可能是申请加倍的空间

8.string


8.1 构造

除了之前介绍的共有构造函数外,string还支持:

	string s(cp,n);			//字符数组cp前n个字符
	string s(s2,pos2);		//从string s2的第pos2个字符开始copy构造
	string s(s2,pos2,len2)	//从string s2的第pos2个字符开始len2个字符
	string s2 = s.substr(len1,len2); //返回一个子串的copy

8.2 insert/erase

string提供接受下标版本的insert和erase

	s.insert(s.size(), 5, '!');	//末端插入5个字符
	s.erase(s.size() - 5, 5);	//删除末端5个字符

接受C风格字符串的赋值和插入

	s.assign(cp, 7);				//赋值为cp指针开始的7个字符
	s.insert(s.size(), cp + 7);		//末端插入cp+7指针指向位置后面的字符

8.3 append/replace

	s.append("...");				//在末端直接插入对应串
	s.replace(pos, len, "..."); 	//从pos开始的len个字符替换为对应串(不需要等长)
	//字符串部分都可以用其他构造函数替代

8.4 搜索

	auto pos = s.find("...");			//返回第一个匹配位置的下标,未找到返回nops(-1)
	auto pos = s.rfind("...");			//返回最后一个匹配位置的下标,未找到返回nops(-1)
	auto pos = s.find_first_of("...");	//返回与模式中任何一个字符相等的第一个位置
	auto pos = s.find_first_not_of("..."); //与上面相反
	auto pos = s.find_last_of("...");	//返回与模式中任何一个字符相等的最后一个位置
	auto pos = s.find_last_not_of("..."); //与上面相反
	//后面参数还可以加上s起始查找位置

找到每个出现的位置:

	string numbers = "...";
	string::size_type pos = 0;
	while((pos = s.find(numbers, pos) != string::npos)
	{
		cout << pos << endl;//找到下一个对应串
		++pos;
	}

8.5 数值转换

	int n = 15;
	string s = to_string(n);	//数字转换成字符串
	double d = stod(s,p,b);		//字符串转换成数字,p是开始转换的子串位置,默认0,b是进制,默认10
	//还有 stoi(),stol()等

9.适配器(adaptor)

包括stackqueuepriority_queue三种
默认情况下,前两种基于deque实现,后面基于vector实现

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值