跳转至

C++的STL库

一、vector动态数组

1、概述

动态数组,也就是不定长数组,数组的长度是可以根据我们的需要动态改变的。

在C++里面有已经写好的标准模板库 STL(Standard Template Library)库,实现了集合、映射表、栈、队列等数据结构和排序、查找等算法。我们可以很方便地调用标准库来减少我们的代码量。

C++ 中动态数组写作vector,而C语言中没有标准库,这也是为什么参加比赛推荐用 C++ 而不用C的原因。

尽量不要在比赛中使用指针!

2、构造

使用vector需要引入<vector>头文件、std命名空间

泛型T是我们数组要储存的数据类型,可以是 intfloatdouble、或者结构体等。初始的时候vector是空的。

#include "vector"
using namespace std;
int main() {
    vector<int> vec;
    return 0;
}

3、使用

1)插入元素

push_back(T t):在尾部插入一个元素

int main() {
    vector<int> vec;
    vec.push_back(1);   // 1
    vec.push_back(2);   // 1 2
    vec.push_back(3);   // 1 2 3
    vec.push_back(4);   // 1 2 3 4
    return 0;
};

2)获取长度与访问元素

size():获取长度

vec[index]:访问元素(类似数组,首位下标是0)

int main() {
    vector<int> vec;
    vec.push_back(1);   // 1
    vec.push_back(2);   // 1 2
    cout << vec.size() << endl; // 2
    cout << vec[1] << endl;     // 2
    return 0;
};

3)修改元素

直接赋值:vec[1] = 1

int main() {
    vector<int> vec;
    vec.push_back(1);   // 1
    cout << vec[0] << endl;     // 1
    vec[0] = 6;
    cout << vec[0] << endl;     // 6
    return 0;
};

4)删除元素

pop_back():删除最后一个元素

int main() {
    vector<int> vec;
    vec.push_back(1);   // 1
    vec.push_back(2);   // 1 2
    vec.pop_back();     // 1
    return 0;
};

5)清空

调用clear()方法就可以清空vector,但不释放内存

vector清空并清除内存的方法

vector<int>().swap(v);

4、构造函数

我们在定义一个对象的时候可以给他赋予初始值。

第一个参数表示初始的动态数组的长度,第二个参数表示初始的数组里面每个元素的值。(如果不传入第二个参数,那么初始的值都是0。)

vector<int> vec(5,1);   // 1 1 1 1 1

5、二维vector

二维动态数组:vector<vector<int> > vec2

<int> >中间有一个空格,这个空格一定要加上,否则在一些老版本的编译器上将不能通过编译。

因为每一维都是空的,我们必须要一维一维的赋值

vector 的维度可以像数组一样更多,但是超过两维以后操作起来会比较麻烦,所以一般用 vector 都只到两维

vector<vector<int> > vec2;
for (int i = 0; i < 5; ++i) {
    vector<int> temp(i + 1, 1);
    vec2.push_back(temp);
}
for (int i = 0; i < 5; i++) {
    for (int j = 0; j < vec2[i].size(); j++) {
        cout << vec2[i][j];
    }
    cout << endl;
}
return 0;

二、set集合

1、概述

集合是由一些不重复的数据组成的。

2、构造

使用set需要引入<set>头文件、std命名空间

泛型T是我们数组要储存的数据类型,可以是 intfloatdouble、或者结构体等。初始的时候map是空的。

#include "set"
using namespace std;
int main() {
    set<int> s;
    return 0;
}

3、使用

1)插入元素

insert()方法向集合中插入一个新的元素。

如果集合中已经存在了某个元素,再次插入不会产生任何效果,集合中是不会出现重复元素的。

set<int> s;
s.insert(1);    // 1
s.insert(2);    // 1 2 
s.insert(1);    // 1 2
s.insert(3);    // 1 2 3

2)删除元素

erase()方法删除集合中的一个元素

如果集合中不存在这个元素,不进行任何操作。

set<int> s;
s.insert(1);    // 1
s.insert(2);    // 1 2 
s.erase(1);     // 2
s.erase(3);    // 2

3)判断元素存在

count()方法判断元素在set中出现的次数。

如果集合中存在我们要查找的元素,返回 1,否则会返回 0。

s.count(3);

4)"增强for"遍历

在C++ 中遍历 set 是从小到大遍历的,也就是说 set 会帮我们排序的。

int main() {
    set<int> s;
    s.insert(1);    // 1
    s.insert(2);    // 1 2
    s.insert(1);    // 1 2
    s.insert(3);    // 1 2 3

    for (auto i: s) {
        cout << i << endl;
    }
    return 0;
}

5)迭代器遍历

C++通过迭代器可以访问集合中的每个元素,迭代器就好像指针指向set中的某个元素。通过操作这个指针,我们可以改变它指向的元素。

*(解引用运算符)操作可以获取迭代器指向的元素。++操作让迭代器指向下个元素,--操作让迭代器指向上一个元素。

迭代器的写法比较固定,set<T>::iterator it就定义了一个指向set<T>这种集合的迭代器itT是任意的数据类型,::iterator 是固定的写法。

begin()返回容器中起始元素的迭代器,end()返回容器的尾后迭代器

int main() {
    set<int> s;
    s.insert(1);    // 1
    s.insert(2);    // 1 2
    s.insert(1);    // 1 2
    s.insert(3);    // 1 2 3

    for (set<int>::iterator it = s.begin(); it != s.end(); it++) {
        cout << *it << endl;
    }
    return 0;
}  

6)反向遍历

rbegin()rend()

int main() {
    set<int> s;
    s.insert(1);    // 1
    s.insert(2);    // 1 2
    s.insert(1);    // 1 2
    s.insert(3);    // 1 2 3

    for (set<int>::iterator it = s.rbegin(); it != s.rend(); it++) {
        cout << *it << endl;
    }
    return 0;
}  

7)清空

clear()方法就可清空 set,同时会清空 set 占用的内存。

set<int> s;
s.clear();

4、set和结构体

set 经常会配合结构体来使用,用set 来储存结构体和 vector 有些区别。正如我们前面所说的那样,set 是需要经过排序的。

系统自带的数据类型有默认的比较大小的规则,而我们自定义的结构体,系统是不可能知道这个结构体比较大小的方式的。

所以我们需要用一种方式来告诉系统怎么比较这个结构体的大小。其中一种方法叫做运算符重载,我们需要重新定义小于符号。

struct Node {
    int x, y;

    bool operator<(const Node &rhs) const {
        if (x == rhs.x) {
            return y < rhs.y;
        } else {
            return x < rhs.x;
        }
    }
};

不要漏掉最后的constconst 函数表示不能对其数据成员进行修改操作,并且const 对象不能调用非const 成员函数,只允许调用 const 成员函数。

三、map映射

1、概述

关键字集合 (key)=> 值集合(value)

2、构造

使用map需要引入<map>头文件、std命名空间。

构造语句:map<T1,T2> m;

#include "map"
using namespace std;
int main() {
    map<string,int> mp;
    return 0;
}

3、pair

insert()方法向集合中插入一个新的映射,參数是一个 pair

pair是一个标准库类型,定义在头文件utility中。可以看成是有两个成员变量 firstsecond 的结构体,并且重载了运算符(先比较 first 大小,如果一样再比较second )。当我们创建一个pair时,必须提供两个类型。

pair<string, int> p;

make_pair(v1, v2)函数:返回由v1v2初始化的pair,类型可以从v1v2的类型推断出来。我们向映射中加入新映射对的时候就是通过插入 pair 来实现的。key已存在,本次插入无效。

4、使用

1)pair插入

key已存在,本次插入无效。

int main() {
    map<string,int> mp;
    mp.insert(make_pair("wc",1));
    mp.insert(make_pair("wmh",1));
    mp.insert(make_pair("wmh",2));  // 无效插入

    // {"wmh" -> 1, "wc" -> 1}
    return 0;
}

2)下标插入与修改

key已存在,下标法能修改值(与make_pair不同)

int main() {
    map<string,int> mp;
    mp["wmh"] = 1;
    mp["wc"] = 2;
    mp["wc"] = 3;

    // {"wmh" -> 1, "wc" -> 3}
    return 0;
}

3)判断映射存在

count(T t)方法判断元素在map中出现的次数。

如果集合中存在我们要查找的元素,返回 1,否则会返回 0。

s.count(3);

4)访问映射

直接利用下标访问:mp["rexhao"]

而这里有一个比较神奇的地方:如果没有对"rexhao"做过映射的话,此时你访问mp["rexhao"],系统将会自动为"rexhao" 生成一个映射,其value为对应类型的默认值(比如int的默认值是 0, string 的默认值是空字符串)

访问映射前需要判断是否存在

int main() {
    map<string, int> mp;
    mp.insert(make_pair("wmh", 1));

    if (mp.count("wmh")) {
        cout << "wmh is in class " << mp["wmh"] << endl;
    } else {
        cout << "cannot find wmh" << endl;
    }

    if (mp.count("wc")) {
        cout << "wc is in class " << mp["wc"] << endl;
    } else {
        cout << "cannot find wc" << endl;
    }
    return 0;
}

5)迭代器遍历

遍历map是按照关键字(key)从小到大遍历的,这一点和set有些共性

for (map<string, int>::iterator it = mp.begin(); it != mp.end(); it++) {
    cout << it->first << " is in class " << it->second << endl;
}

也可以使用auto类型

for (auto it = mp.begin(); it != mp.end(); it++) {
    cout << it->first << " is in class " << it->second << endl;
}

6)"增强for"遍历

for(auto it : mp){
    cout << it.first <<" "<< it.second <<endl;
}

5、二维map

1)map套用set

map套用set:map<int, set<string> > s;

  • 插入:s[2].insert("rexhao");
  • 查询:s[2].count("rexhao");
  • 删除:s[2].erase("rexhao");

2)map套用map

map套用map:map<int,map<string, int> > s;

  • rexhao每来2班一次:s[2]["rexhao"]++;
  • 查询rexhao来过2班的次数:cout << s[2]["rexhao"] << endl;

二维的迭代器遍历

/**
 * 二维 map 遍历
 * @author  王铭颢
 * @Date  2022/11/23 13:29
 */

#include "iostream"
#include "map"

using namespace std;

int main() {
    /**
     * int1:班级
     * string:姓名
     * int2:xxx来xx班的次数
     */
    map<int, map<string, int> > mp;
    // 数据初始化
    mp[2]["wmh"] = 5;
    mp[3]["wmh"] = 3;
    mp[4]["wmh"] = 5;
    mp[2]["wcc"] = 1;

    for (map<int, map<string, int> >::iterator it = mp.begin(); it != mp.end(); it++) {
        for (map<string, int>::iterator itt = it->second.begin(); itt != it->second.end(); itt++) {
            cout << it->first << ":" << itt->first << ":" << itt->second << endl;
        }
    }
    // 也可以使用auto类型
    return 0;
}