C++:常用stl容器及其操作

C++相较于其他语言一个显著的优点为,它封装了一些stl(standared template library,标准模板库)容器,使用起来就不需要自己动手实现了,就可以节省更多时间用于思考代码的思路以及代码主要部分。本篇文章介绍常用的stl容器极其操作。

1、vector(变长数组)

要使用vector容器,首先要包含头文件<vector>,其次,要声明一个vector,应该使用==vector<数据类型>==的方式,此外,还有一些初始化操作:

vector<int> a  //初始为空
vector<int> a(10)  //初始有10个位置
vector<int> a(10, 3)   //初始有10个位置,且都被初始化为了3
vector<int> a[10]   //声明一个vector数组,每个数组元素都是vector<int>

常用vector操作:

  1. size():返回vector元素数量
  2. empty():返回vector是否为空(空为1,不空为0)
  3. clear():清空vector
  4. front():返回首元素
  5. back():返回尾元素
  6. push_back(a):在尾插入a
  7. pop_back():弹出尾元素
  8. begin():迭代器,可以看作指向第一个元素的指针
  9. end() :迭代器,可以看作指向最后一个元素的后一个位置的指针
  10. [a]:支持随机寻址,返回下标为a的元素(下标从0开始)

遍历vector有三种常用操作

  1. 用下标
  2. 用迭代器
  3. 用范围for语句

演示一下,执行以下代码:

#include <iostream>
#include <vector>

using namespace std;

int main()
{
    vector<int> a;

    for (int i = 1; i <= 10; i ++ ) a.push_back(i);

    //用下标
    for (int i = 0; i < 10; i ++ ) cout << a[i] << ' ';
    cout << endl;

    //用迭代器
    for (auto i = a.begin(); i != a.end(); i ++ ) cout << *i << ' ';
    cout << endl;

    //用范围for语句
    for (auto x : a) cout << x << ' ';
    cout << endl;

    return 0;
}

得到:
在这里插入图片描述

还有必要说一下vector变长的倍增思想。首先要说一下计算机分配内存的时间,每次分配内存和大小关系不大,和分配次数有很大关系。比如一次分配1000个位置,和1000次分配1个位置,后者要慢很多很多。所以vector在变长的时候,要尽量减少分配次数。在gcc环境下,假设当前有n个位置,如果用的位置超过了n个,那就申请2n个位置,并把前n个位置上的元素复制到新分配的前n个里。平均下来,vector插入一个数的时间复杂度是O(1)的。且这样分配次数为logn,有效减少申请次数。用程序展示一下:

#include <iostream>
#include <vector>

using namespace std;

int main()
{
    vector<int> a;

    while (1)
    {
        int x;
        cin >> x;
        a.push_back(x);

        cout << "当前元素数量" << a.size() << endl;
        cout << "当前分配空间" << a.capacity() << endl;
    }
}

在这里插入图片描述

2、string(字符串)

C++对字符串进行了封装,让字符串的操作更加简单。要使用string类,要包含<string>头文件。初始化string可以直接使用赋值运算符。

常用string操作

  1. size():返回字符串长度
  2. empty():返回string是否为空(空为1,不空为0)
  3. clear():清空string
  4. length():和size()完全一样
  5. substr():截取子串
  6. c_str():返回字符串地址(首字符地址)

substr()的用法:substr接受两个参数,substr(a, b),a表示截取子串的其实下标,b表示截取长度。如果越界,则截取到最后停止。此外,substr还可以只接受一个参数a,那么就从下标a开始截取到最后。
一些额外的操作:string还可以使用+=操作。

举例:

#include <iostream>
#include <string>

using namespace std;

int main()
{
    string a = "abcdef";
    string b = a;

    string c = a.substr(1, 3);
    cout << c << endl;

    cout << a.substr(2) << endl;

    cout << endl;

    b += "1234";
    cout << b << endl;

    b += '*';
    cout << b << endl;

    return 0;
}

输出:
在这里插入图片描述

3、queue(队列)

使用queue容器,要包含<queue>头函数。就是一个已经封装好的先进先出的队列。

常用queue操作

  1. size():返回队列长度
  2. empty():返回队列是否为空(空为1,不空为0)
  3. push(a):在队尾插入a
  4. front():返回队头元素
  5. back():返回队尾元素
  6. pop():弹出队头元素

注意,queue没有clear操作。但是也可以用重新构造的方法清空队列:

#include <iostream>
#include <queue>

using namespace std;

int main()
{
    queue<int> q;
    q.push(1), q.push(2);
    cout << q.front() << endl;

    //重新构造来清空队列
    q = queue<int>();
    if (q.empty()) cout << "队列为空" << endl;

    return 0;
}

在这里插入图片描述

4、priority_queue(优先队列)

优先队列其实就是一个堆。要使用priority_queue,需要包含头文件<queue>。

常用priority_queue操作

  1. size():返回堆元素数量
  2. empty():返回堆是否为空(空为1,不空为0)
  3. push(a):向堆中加入一个元素a
  4. top():返回堆顶元素
  5. pop():弹出堆顶元素

默认情况下,priority_queue构造的是一个大根堆。但是有一个固定的语法构造小根堆,要包含<vector>头文件。这里以int类型举个例子:

#include <iostream>
#include <queue>
#include <vector>

using namespace std;

int main()
{
    //默认大根堆
    priority_queue<int> q1;
    for (int i = 1; i <= 10; i ++ ) q1.push(i);

    //构造小根堆
    priority_queue<int, vector<int>, greater<int>> q2;
    for (int i = 1; i <= 10; i ++ ) q2.push(i);

    cout << q1.top() << endl << q2.top() << endl;

    return 0;
}

在这里插入图片描述
要改变存储的数据类型,只要改变3个int即可。注意,priority_queue也没有clear操作。

5、stack(栈)

使用stack容器,要包含头文件<stack>。就是一个封装好的栈。

常用stack操作

  1. size():返回栈元素数量
  2. empty():返回栈是否为空(空为1,不空为0)
  3. push(a):在栈顶加入a
  4. top():返回栈顶元素
  5. pop():弹出栈顶元素

注意,stack没有clear操作。另外,栈的所有操作都是O(1)的。

6、deque(双端队列)

要使用deque容器,要包含头文件<deque>。deque是一个功能很强的容器,可以看作是升级的vector。但是缺点就是它的操作太慢了

常用deque操作

  1. size():返回deque元素数量
  2. empty():返回deque是否为空(空为1,不空为0)
  3. clear():清空deque
  4. front():返回首元素
  5. back():返回尾元素
  6. push_back(a):在结尾加入元素a
  7. pop_back():弹出尾元素
  8. push_front(a):在开头加入元素a
  9. pop_front():弹出首元素
  10. begin() :迭代器
  11. end():迭代器

同时,deque支持随机寻址操作,即取下标。

7、set(集合)以及multiset(多重集合)

要使用set和multiset容器,要包含头文件<set>。它们是基于红黑树(一种特殊的平衡二叉树)实现的动态维护有序序列的容器。区别就是,set不能存储重复值,但是multiset可以存储重复值。

常用set和multiset操作

  1. size():返回集合元素数量
  2. empty():返回集合是否为空(空为1,不空为0)
  3. clear():清空集合
  4. insert(a):插入一个数a,O(logn)
  5. find(a):返回a在集合中的个数。如果是set,则只可能是0、1,如果是multiset,那么可以是其他
  6. erase(a):如果a是一个值,那么删除所有a;如果a是迭代器,那么删除迭代器位置元素,O(k + logn),其中k是待删除元素个数
  7. lower_bound(a):返回 大于等于 a的最小元素的迭代器。如果不存在,返回end()
  8. upper_bound(a):返回 大于 a的最小元素的迭代器。如果不存在返回end()
  9. count(a):返回元素 i 在集合中出现的次数。由于set容器仅包含唯一元素,因此只能返回1或0;multiset中可返回多个。

8、map(映射)和multimap(多重映射)

要使用map和multimap容器,要包含头文件<map>。它们是基于红黑树(一种特殊的平衡二叉树)实现的动态维护有序序列的容器。区别就是,map不能存储重复值,但是multimap可以存储重复值。
它们和set的区别就是,这俩存储的是一对关联值pair。

常用map和multimap操作

  1. size():返回映射元素数量
  2. empty():返回映射是否为空(空为1,不空为0)
  3. clear():清空映射
  4. insert({a, b}):插入一个pair{a, b}
  5. erase():输入pair或迭代器,效果同set
  6. lower_bound(a):返回 大于等于 a的最小元素的迭代器,比较时依据类型1。如果不存在,返回end()
  7. upper_bound(a):返回 大于 a的最小元素的迭代器,比较时依据类型1。如果不存在返回end()

map和multimap还支持一个很强的操作:像使用数组一样使用map。意思是:a[类型一元素] = 类型二元素

举个例子:

#include <iostream>
#include <map>
#include <string>

using namespace std;

int main()
{
    map<string, int> a;

    a["abc"] = 1;
    a["bcd"] = 2;
    a["cde"] = 3;

    cout << (*a.upper_bound("a")).second << endl;
    cout << (*a.upper_bound("b")).second << endl;
    

    return 0;
}

在这里插入图片描述

10、unordered_set、unordered_multiset、unordered_map以及unordered_multimap

这一坨容器是基于哈希表实现的。要使用unordered_set和unordered_multiset容器,要包含头文件<unordered_set>。要使用unordered_map和unordered_multimap容器,要包含头文件<unordered_map>。它们和上面四个容器类似,不同之处在于这几个是unordered的,也即无序的。所以,它们不支持lower_bound和upper_bound操作。但是好处就是,因为不需要维护顺序,所以这几个的速度会比上面那几个快很多。各有利弊。

11、bitset(压位存储)

使用的话,要包含头文件<bitset>。用处就是可以把每个数据压缩到只有一位的大小。

最常用的用处就是,当我们需要一个10000 * 10000的bool矩阵时,如果存成bool类型,那就是108个字节(C++中bool类型占一个字节),直接爆内存了。但是如果使用压位存储,那就只需要108位了,减少了内存消耗,并且能满足需求。

#include <iostream>
#include <bitset>

using namespace std;

int main()
{
	//声明时,括号内是位数
    bool a[1000][1000];
    bitset<1000> b[1000];

    cout << sizeof a << endl << sizeof b << endl;

    return 0;
}

在这里插入图片描述
可以看到内存明显小了很多。

常用bitset操作

  1. count():返回1的个数
  2. any():返回 是否至少有一个1
  3. none():返回 是否全为0
  4. set():将所有位置为1,如果是set(k, v),则将第k位置为1
  5. reset():将所有位置为0
  6. flip():将所有位取反,如果是flip(k),则将第k位取反

此外,bitset还支持~(非)、&(与)、|(或)、^(异或)、>>(右移位)和<<(左移位)操作。

  • 3
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值