STL标准模板库

目录

一 STL标准模板库

  • STL主要分为三类:
    • algorithm(算法): 对数据进行处理(解决问题) 步骤的有限集合
    • container(容器): 用来管理一组数据元素
    • Iterator (迭代器):可遍历STL容器内全部或部分元素”的对象

STL被组织为下面的13个头文件:
<algorithm>、<deque>、<functional>、<iterator>、<vector>、<list>、<map>、<memory>、<numeric>、<queue>、<set>、<stack> 和<utility>

#include <iostream>
#include <algorithm>
#include <vector>

using namespace std;

// 普通类型
int main()
{
    // 容器
    vector<int> v1;
    v1.push_back(1);
    v1.push_back(2);
    v1.push_back(3);
    v1.push_back(4);
    v1.push_back(2);
    v1.push_back(2);

    cout << "容器的大小:" << v1.size() << endl;

    // 1.采用下标访问
    cout << "下标访问:";
    for (unsigned int i = 0; i < v1.size(); i++)
    {
        cout << v1[i] << " ";
    }
    cout << endl;

    // 2.迭代器访问
    cout << "迭代器访问:";
    vector<int>::iterator it = v1.begin();
    for (; it != v1.end(); it++)
    {
        cout << *it << " ";
    }
    cout << endl;

    // 算法
    int value = 2;
    int ncount = count(v1.begin(), v1.end(), value);
    cout << "容器中数值为" << value << "的个数:" << ncount << endl;

    return 0;
}

image

#include <iostream>
#include <vector>

using namespace std;

// 类类型
class Student
{
public:
    Student(int age, const char* name)
    {
        m_age = age;
        strcpy_s(m_name, 64, name);
    }
    void print() const
    {
        cout << "姓名:" << m_name << " 年龄:" << m_age << endl;
    }
private:
    int m_age;
    char m_name[64];
};

int main()
{
    // vector<Student> v1,vector是值传递,会调用拷贝构造
    vector<Student*> v1;  // 这里使用指针,就不会调用拷贝构造
    Student s1(19, "张三");
    Student s2(20, "李四");
    Student s3(31, "王五");

    v1.push_back(&s1);
    v1.push_back(&s2);
    v1.push_back(&s3);

    cout << "下标访问法:" << endl;
    for (unsigned int i = 0; i < v1.size(); i++)
    {
        v1[i]->print();
    }
    cout << endl;

    cout << "迭代器访问法:" << endl;
    for (vector<Student*>::iterator it = v1.begin(); it != v1.end(); it++)
    {
        (*it)->print();
    }

    return 0;
}

image

二 容器

容器部分主要有由<vector>,<list>,<deque>,<set>,<map>,<stack> 和<queue>组成。

下面是常用的一些容器,可以通过下表总结一下它们和相应头文件的对应关系。

数据结构 描述 实现头文件
向量(vector) 连续存储的元素 <vector>
列表(list) 由节点组成的双向链表,每个结点包含着一个元素 <list>
双向队列(deque) 连续存储的指向不同元素的指针所组成的数组 <deque>
集合(set) 由节点组成的红黑树,每个节点都包含着一个元素,节点之间以某种作用于元素对的谓词排列,没有两个不同的元素能够拥有相同的次序 <set>
多重集合(multiset) 允许存在两个次序相等的元素的集合 <set>
栈(stack) 后进先出的元素的排列 <stack>
队列(queue) 先进先出的元素的排列 <queue>
优先队列(priority_queue) 元素的次序是由作用于所存储的值对上的某种优先级决定的的一种队列 <queue>
映射(map) 由{键,值}对组成的集合,以某种作用于键对上的谓词排列 <map>
多重映射(multimap) 允许键对有相等的次序的映射 <map>

三 vector容器 动态数组

vector是将元素置于一个动态数组中加以管理的容器。

vector可以随机存取元素,支持索引值直接存取, 用 [] 操作符或 at()方法 对元素进行操作
image

vector尾部添加或移除元素非常快速。但是在中部或头部插入元素或移除元素比较费时
image

3.1 vector的构造函数

当我们使用vector的默认构造函数时,不能直接通过下标进行访问, 可以先pushback数据,再用下标访问

// 默认构造函数
vector<int> v1;

// 当我们使用vector的默认构造函数时,不能直接通过下标进行访问,这时还没有进行扩容
//v1[0] = 1;  //报错 vector subscript out of range

image

带参构造函数,构造时就分配空间,同时插入元素

#include <iostream>
#include <vector>

using namespace std;

int main()
{
    // 带参构造函数
    vector<int> v1(10);  // 构造时就分配空间,同时插入10个为0的元素

    cout << "v1的元素个数:" << v1.size() << endl;
    cout << "v1的容器大小:" << v1.capacity() << endl;

    v1.push_back(1); 
    for (int i = 0; i < v1.size(); i++)
    {
        cout << v1[i] << " ";
    }
    cout << endl;
    cout << "v1的元素个数:" << v1.size() << endl;
    //当 vector 容器的大小和容量相等时,如果再向其添加(或者插入)一个元素,
    //vector 往往会申请多个存储空间,而不仅仅只申请 1 个
    cout << "v1的容器大小:" << v1.capacity() << endl;

    return 0;
}

image

带参构造函数

#include <iostream>
#include <vector>

using namespace std;

int main()
{
    // 带参构造函数
    vector<int> v1(10,666);  
    vector<int> v2(v1);
    vector<int> v3(v1.begin()+5, v1.end());
    int test[] = { 0,1,2,3,4,5 };
    vector<int> v4(test, test + 4);

    cout << "v1中的元素:";
    for (int i = 0; i < v1.size(); i++)
    {
        cout << v1[i] << " ";
    }
    cout << endl;

    cout << "v2中的元素:";
    for (int i = 0; i < v2.size(); i++)
    {
        cout << v2[i] << " ";
    }
    cout << endl;

    cout << "v3中的元素:";
    for (int i = 0; i < v3.size(); i++)
    {
        cout << v3[i] << " ";
    }
    cout << endl;

    cout << "v4中的元素:";
    for (int i = 0; i < v4.size(); i++)
    {
        cout << v4[i] << " ";
    }
    cout << endl;
    return 0;
}

image

3.2 vector的赋值

#include <iostream>
#include <vector>

using namespace std;

int main()
{
    // 带参构造函数
    vector<int> v1, v2, v3, v4;
    int arr[] = { 0,1,2,3,4,5 };

    v1.assign(5, 888);               // 第一种玩法,改变原来vector中元素的个数和值
    v2.assign(v1.begin(),v1.end());  // 第二种玩法,使用迭代器赋值
    v3.assign(arr, arr + 4);         // 第三种玩法,使用指针赋值
    v4 = v3;                         // 第四种玩法,赋值运算

    cout << "v1中的元素:";
    for (int i = 0; i < v1.size(); i++)
    {
        cout << v1[i] << " ";
    }
    cout << endl;

    cout << "v2中的元素:";
    for (int i = 0; i < v2.size(); i++)
    {
        cout << v2[i] << " ";
    }
    cout << endl;

    cout << "v3中的元素:";
    for (int i = 0; i < v3.size(); i++)
    {
        cout << v3[i] << " ";
    }
    cout << endl;

    cout << "v4中的元素:";
    for (int i = 0; i < v4.size(); i++)
    {
        cout << v4[i] << " ";
    }
    cout << endl;


    return 0;
}

image

3.3 vector的大小

  • vector.size(); 返回容器中元素的个数
  • vector.empty(); 判断容器是否为空
  • vector.resize(num); 重新指定容器的长度为num,若容器变长,则以默认值填充新位置。如果容器变短,则末尾超出容器长度的元素被删除。
  • vector.resize(num, elem); 重新指定容器的长度为num,若容器变长,则以elem值填充新位置。如果容器变短,则末尾超出容器长度的元素被删除
#include <iostream>
#include <vector>

using namespace std;

int main()
{
 
    int arr[] = { 0,1,2,3,4,5,6,7,8,9 };
    vector<int> v1(arr, arr+10);

    cout << "v1中元素的个数:" << v1.size() << endl;
    cout << "v1中的元素:";
    for (int i = 0; i < v1.size(); i++)
    {
        cout << v1[i] << " ";
    }
    cout << endl;

    v1.resize(5);        // 重新定义大小,v1.resize(5, 88)缩容时后面加上指定值没有作用
    cout << endl << "----- 执行 v1.resize(5) 后 -----" << endl;
    cout << "v1中元素的个数:" << v1.size() << endl;
    cout << "v1中的元素:";
    for (int i = 0; i < v1.size(); i++)
    {
        cout << v1[i] << " ";
    }
    cout << endl;

    v1.resize(8, 66);   // 重新定义大小,扩容时指定新增的值,如果不指定值,默认为0
    cout << endl << "----- 执行 v1.resize(8, 66) 后 -----" << endl;
    cout << "v1中元素的个数:" << v1.size() << endl;
    cout << "v1中的元素:";
    for (int i = 0; i < v1.size(); i++)
    {
        cout << v1[i] << " ";
    }
    cout << endl;
    return 0;
}

image

3.4 vector末尾的添加移除操作

v2.push_back(1); //在容器尾部加入一个元素
v2.pop_back(); //移除容器中最后一个元素

3.5 vector的数据存取

第一 使用下标操作 v2[0] = 100;
第二 使用at 方法 如: v2.at(2) = 100;
第三 接口返回的引用 v2.front() 和 v2.back()
注意: 第一和第二种方式必须注意越界

3.6 vector的插入

vector.insert(pos,elem); //在pos位置插入一个elem元素的拷贝,返回新数据的位置。
vector.insert(pos,n,elem); //在pos位置插入n个elem数据,无返回值。
vector.insert(pos,beg,end); //在pos位置插入[beg,end)区间的数据,无返回值

3.6 vector的删除

1.把整个vector 都删掉

v2.clear();
cout<<"调用 v2.clear() 后"<<endl;

2.删掉单个元素

v2[1] = 888;
v2.erase(v2.begin()+1);

3.删掉多个元素

v2.erase(v2.begin(), v2.begin()+3);

3.7 c++11 emplace

#include <iostream>
#include <vector>
#include <string>
using namespace std;

class Student
{
public:
    Student():m_name("未命名"),m_age(0) 
    { 
        cout << "调用默认构造函数" << endl;
    }
    Student(string name, int age) :m_name(name), m_age(age)
    {
        cout << "调用带参构造函数" << endl;
    }
    Student(const Student& other)
    {
        m_name = other.m_name;
        m_age = other.m_age;
        cout << "调用拷贝构造函数" << endl;
    }
    ~Student()
    {
        cout << "调用析构函数" << endl;
    }

    void print() const
    {
        cout << "姓名:" << m_name << "年龄:" << m_age << endl;
    }
private:
    string m_name;
    int m_age;
};

int main()
{
    vector<Student> vetStu;

    // 先插入7条数据,使得此时容器的容量是 9,再测试push_back和emplace的时候不会再进行扩容
    vetStu.push_back(Student("小王", 18));
    vetStu.push_back(Student("小王", 18));
    vetStu.push_back(Student("小王", 18));
    vetStu.push_back(Student("小王", 18));
    vetStu.push_back(Student("小王", 18));
    vetStu.push_back(Student("小王", 18));
    vetStu.push_back(Student("小王", 18));
    cout << "size:" << vetStu.size() << " capacity:" << vetStu.capacity() << endl;

    // 使用push_back
    cout << endl <<"test push_back" << endl;
    vetStu.push_back(Student("小明", 19));
    cout << "size:" << vetStu.size() << " capacity:" << vetStu.capacity() << endl;

    // 使用emplace_back
    cout << endl << "test emplace_back" << endl;
    vetStu.emplace_back("小红",20);
    cout << "size:" << vetStu.size() << " capacity:" << vetStu.capacity() << endl;

    cout << endl << "打印所有学生信息:" << endl;
    for (vector<Student>::iterator it = vetStu.begin(); it != vetStu.end(); it++)
    {
        it->print();
    }
    return 0;
}

输出信息截图没有截完整,只用看所标注的地方,emplace_back的效率要比push_back的效率高,少调用一次拷贝构造函数
image

函数 对应
emplace insert
emplace_back push_back

四 deque 容器 双端数组

deque是“double-ended queue”的缩写,和vector一样都是STL的容器,唯一不同的是:deque是双端数组,而vector是单端的。
image
Deque 特点:
deque在接口上和vector非常相似,在许多操作的地方可以直接替换。
deque可以随机存取元素(支持索引值直接存取,用[]操作符或at()方法)
deque头部和尾部添加或移除元素都非常快速, 但是在中部安插元素或移除元素比较费时。

使用时,包含头文件:#include <deque>

4.1 deque对象的构造

// deque对象的默认构造
deque\<int> deqInt;            //存放int的deque容器。
deque\<float> deqFloat;        //存放float的deque容器。
deque\<student> deqStu;        //存放student的deque容器。

//deque对象的带参数构造
deque(beg,end);    //构造函数将 [beg, end) 区间中的元素拷贝给本身。
deque(n,elem);     //构造函数将 n 个 elem 拷贝给本身。
deque(const deque  &deq);  //拷贝构造函数。

deque<int> deqIntA;
deqIntA.push_back(1);
deqIntA.push_back(2);
deqIntA.push_back(3);
deqIntA.push_back(4);

deque<int> deqIntB(deqIntA.begin(),deqIntA.end());  //1 2 3 4 
deque<int> deqIntC(5, 666);                         //666 666 666 666 666
deque<int> deqIntD(deqIntA);                        //1 2 3 4

4.2 deque头部和末尾的添加移除操作

  • deque.push_back(element); //容器尾部添加一个数据
  • deque.push_front(element); //容器头部插入一个数据
  • deque.pop_back(); //删除容器最后一个数据
  • deque.pop_front(); //删除容器第一个数据
deque<int> deqIntA;
deqIntA.push_back(1);
deqIntA.push_back(2);
deqIntA.push_back(3);
deqIntA.push_back(4);
deqIntA.push_back(5);
deqIntA.push_back(6);  // 1 2 3 4 5 6 
deqIntA.pop_front();   // 2 3 4 5 6
deqIntA.pop_front();   // 3 4 5 6 
deqIntA.push_front(7); // 7 3 4 5 6
deqIntA.push_front(8); // 8 7 3 4 5 6
deqIntA.pop_back();    // 8 7 3 4 5 
deqIntA.pop_back();    // 8 7 3 4 

4.3 deque的数据存取

1.使用下标操作 deqIntA[0] = 100;
2.使用at 方法 如: deqIntA.at(2) = 100;
3.接口返回的引用 deqIntA.front() 和 deqIntA.back()
注意:第一和第二种方式必须注意越界

例如:

deque<int> deqIntA;
deqIntA.push_back(1);
deqIntA.push_back(2);
deqIntA.push_back(3);
deqIntA.push_back(4);
deqIntA.push_back(5);

int i1 = deqIntA.at(0); //i1 = 1
int i2 = deqIntA[1]; //i2 = 2
deqIntA.at(0) = 666; //第一个元素改成666
deqIntA[1] = 888; //第二个元素改成888

int iFront = deqInt.front(); //666
int iBack = deqInt.back(); //5
deqInt.front() = 888; //第一个元素改成  888
deqInt.back() = 666; //最后一个元素改成 666

4.4 deque与迭代器

  • deque.begin(); //返回容器中第一个元素的迭代器。
  • deque.end(); //返回容器中最后一个元素之后的迭代器。
  • deque.rbegin(); //返回容器中倒数第一个元素的迭代器。
  • deque.rend(); //返回容器中倒数最后一个元素之后的迭代器。
  • deque.cbegin(); //返回容器中第一个元素的常量迭代器。
  • deque.cend(); //返回容器中最后一个元素之后的常量迭代器。
deque<int> deqIntA;
deqIntA.push_back(1);
deqIntA.push_back(2);
deqIntA.push_back(3);
deqIntA.push_back(4);
deqIntA.push_back(5);

//普通迭代器
for(deque<int>::iterator it = deqIntA.begin(); it!=deqIntA.end(); ++it)
{
    //(*it)++;  //23456
    cout<<*it;
    cout<<" ";
}

//常量迭代器
deque<int>::const_iterator cit = deqIntA.cbegin();
for( ; cit!=deqIntA.cend(); cit++)
{
    cout<<*cit;
    cout<<" ";
}

//逆转的迭代器
for(deque<int>::reverse_iterator rit=deqIntA.rbegin(); rit!=deqIntA.rend(); ++rit)
{
    cout<<*rit;
    cout<<" ";
}

4.5 deque的赋值

  • deque.assign(beg,end); //将 [beg, end)区间中的数据拷贝赋值给本身。注意该区间是左闭右开的区间。
  • deque.assign(n,elem); //将n个elem拷贝赋值给本身。
  • deque& operator=(const deque &deq); //重载等号操作符
  • deque.swap(deq); // 将deque与本身的元素互换

例如:

deque<int> deqIntA,deqIntB,deqIntC,deqIntD;

deqIntA.push_back(1);
deqIntA.push_back(2);
deqIntA.push_back(3);
deqIntA.push_back(4);
deqIntA.push_back(5);


deqIntB.assign(deqIntA.begin(),deqIntA.end()); // 1 2 3 4 5 
deqIntC.assign(4,888); //888 888 888 888 
deqIntD = deqIntA; //1 2 3 4 5 
deqIntC.swap(deqIntD); //互换 deqIntC:1 2 3 4 5   deqIntD:888 888 888 888 

4.6 deque的大小

  • deque.size(); //返回容器中元素的个数
  • deque.empty(); //判断容器是否为空
  • deque.resize(num); //重新指定容器的长度为num,若容器变长,则以默认值0填充新位置。如果容器变短,则末尾超出容器长度的元素被删除。
  • deque.resize(num, elem); //重新指定容器的长度为num,若容器变长,则以elem值填充新位置。如果容器变短,则末尾超出容器长度的元素被删除。
deque<int> deqIntA;
deqIntA.push_back(1);
deqIntA.push_back(2);
deqIntA.push_back(3);
deqIntA.push_back(4);
deqIntA.push_back(5);

int iSize = deqIntA.size();  //5

deqIntA.resize(7); //1 2 3 4 5 0 0 
deqIntA.resize(8,1); //1 2 3 4 5 0 0 1
deqIntA.resize(2); //1 2

47 deque的插入

  • deque.insert(pos,elem); //在pos位置插入一个elem元素的拷贝,返回新数据的位置。
  • deque.insert(pos,n,elem); //在pos位置插入n个elem数据,无返回值。
  • deque.insert(pos,beg,end); //在pos位置插入[beg,end)区间的数据,无返回值
#include <deque>
#include <iostream>

using namespace std;

int main(void)
{
    deque<int> deqIntA;
    deque<int> deqIntB;

    deqIntA.push_back(1);
    deqIntA.push_back(2);
    deqIntA.push_back(3);
    deqIntA.push_back(4);  // 1 2 3 4

    deqIntB.push_back(11);
    deqIntB.push_back(12);
    deqIntB.push_back(13);
    deqIntB.push_back(14); // 11 12 13 14

    deqIntA.insert(deqIntA.begin(), 0); // {0,1,2,3,4}
    deqIntA.insert(deqIntA.begin()+1, 2, 88);  //{0,88,88,1,2,3,4}

    deqIntA.insert(deqIntA.begin(), deqIntB.rbegin(), deqIntB.rend()); //{11,12,13,14,0,88,88,1,2,3,4}

    for(deque<int>::iterator it = deqIntA.begin(); it!=deqIntA.end(); ++it)
    {
        cout<<*it;
        cout<<" ";
    }

    return 0;
}

4.8 deque的删除

  • deque.clear(); //移除容器的所有数据
  • deque.erase(beg,end); //删除[beg,end)区间的数据,返回下一个数据的位置。
  • deque.erase(pos); //删除pos位置的数据,返回下一个数据的位置。
#include <deque>
#include <iostream>

using namespace std;

int main(void)
{
    deque<int> deqIntA;

    deqIntA.push_back(1);
    deqIntA.push_back(2);
    deqIntA.push_back(3);
    deqIntA.push_back(4);
    deqIntA.push_back(5);

    //方式一 单独使用擦除的接口
    //deqIntA.erase(deqIntA.begin()+1); //干掉第二个元素 {1,3,4,5}

    //deqIntA.erase(deqIntA.begin()+1, deqIntA.begin()+3);// 干掉3 和4, 剩下{1, 5}

    //deqIntA.clear(); //干掉所有的元素

    //方式二 使用迭代器遍历删除
    for(deque<int>::iterator it = deqIntA.begin(); it!=deqIntA.end();)
    {
        if(*it == 4)
        {
            it = deqIntA.erase(it);
        }
        else 
        {
            cout<<*it;
            cout<<" ";
            it++;
        }
    }

    return 0;
}

五 list 容器

list是一个双向链表容器,可高效地进行插入删除元素。
image

List 特点:
list不可以随机存取元素,所以不支持at.(position)函数与[]操作符。可以对其迭代器执行++,但是不能这样操作迭代器:it+3
使用时包含 #include <list>

5.1 list对象的构造

list同样采用模板类实现,对象的默认构造形式:list<T> listT; 如:

//注意:尖括号内还可以设置指针类型或自定义类型。
list<int> lstInt;            //定义一个存放int的list容器。
list<float> lstFloat;        //定义一个存放float的list容器。
list<string> lstString;      //定义一个存放string的list容器。

list对象的带参数构造

list(beg,end);           //将[beg, end)区间中的元素拷贝给本身。
list(n,elem);            //构造函数将n个elem拷贝给本身。
list(const list &lst);   //拷贝构造函数。

示例:

list<int> lstInt1;
lstInt1.push_back(1);
lstInt1.push_back(2);
lstInt1.push_back(3);

list<int> lstInt2(lstInt1.begin(),lstInt1.end()); //1 2 3 
list<int> lstInt3(5,8);  //8 8 8 8 8
list<int> lstInt4(lstIntA);  //1 2 3 

5.2 list头尾的添加移除操作

  • list.push_back(elem); //在容器尾部加入一个元素
  • list.pop_back(); //删除容器中最后一个元素
  • list.push_front(elem); //在容器开头插入一个元素
  • list.pop_front(); //从容器开头移除第一个元素
list<int> lstInt;
lstInt.push_back(1);
lstInt.push_back(2);
lstInt.push_back(3);
lstInt.push_back(4);
lstInt.push_back(5);
lstInt.pop_front();
lstInt.pop_front();
lstInt.push_front(11);
lstInt.push_front(12);
lstInt.pop_back();
lstInt.pop_back();
// lstInt    {12, 11, 3}

5.3 list的数据存取

  • list.front(); //返回第一个元素。
  • list.back(); //返回最后一个元素。
list<int> lstInt;
lstInt.push_back(1);
lstInt.push_back(2);
lstInt.push_back(3);
lstInt.push_back(4);
lstInt.push_back(5);

int iFront = lstInt.front();//1
int iBack = lstInt.back();//5
lstInt.front() = 11;//11
lstInt.back() = 19;//19

5.4 list与迭代器

  • list.begin(); //返回容器中第一个元素的迭代器。
  • list.end(); //返回容器中最后一个元素之后的迭代器。
  • list.rbegin(); //返回容器中倒数第一个元素的迭代器。
  • list.rend(); //返回容器中倒数最后一个元素的后面的迭代器。
  • list.cbegin(); //返回容器中第一个元素的常量迭代器。
  • list.cend(); //返回容器中最后一个元素之后的常量迭代器。
list<int> lstInt;
lstInt.push_back(1);
lstInt.push_back(3);
lstInt.push_back(5);
lstInt.push_back(7);
lstInt.push_back(9);

for (list<int>::iterator it=lstInt.begin(); it!=lstInt.end(); ++it)
{
    cout << *it;
    cout << " ";
}

for (list<int>::reverse_iterator rit=lstInt.rbegin(); rit!=lstInt.rend(); ++rit)
{
    cout << *rit;
    cout << " ";
}

5.5 list的赋值

  • list.assign(beg,end); //将[beg, end)区间中的数据拷贝赋值给本身。
  • list.assign(n,elem); //将n个elem拷贝赋值给本身。
  • list& operator=(const list &lst); //重载等号操作符。
  • list.swap(lst); // 将lst与本身的元素互换。
llist<int> lstIntA,lstIntB,lstIntC,lstIntD;
lstIntA.push_back(1);
lstIntA.push_back(3);
lstIntA.push_back(5);
lstIntA.push_back(7);
lstIntA.push_back(9);

lstIntB.assign(lstIntA.begin(),lstIntA.end()); //1 3 5 7 9
lstIntB.assign(++lstIntA.begin(),--lstIntA.end()); //3 5 7

lstIntC.assign(5,8); //8 8 8 8 8
lstIntD = lstIntA; //1 3 5 7 9
lstIntC.swap(lstIntD); //互换

5.6 list的大小

  • ist.size(); //返回容器中元素的个数
  • list.empty(); //判断容器是否为空
  • list.resize(num); //重新指定容器的长度为num,若容器变长,则以默认值0填充新位置。如果容器变短,则末尾超出容器长度的元素被删除。
  • list.resize(num, elem); //重新指定容器的长度为num,若容器变长,则以elem值填充新位置。如果容器变短,则末尾超出容器长度的元素被删除。
list<int> lstIntA;
lstIntA.push_back(1);
lstIntA.push_back(2);
lstIntA.push_back(3);

if (!lstIntA.empty())
{
    int iSize = lstIntA.size(); //3
    lstIntA.resize(5); //1 2 3 0 0
    lstIntA.resize(7,1); //1 2 3 0 0 1 1
    lstIntA.resize(5); //1 2 3 0 0
}

5.7 list的插入

  • list.insert(pos,elem); //在pos位置插入一个elem元素的拷贝,返回新数据的位置。
  • list.insert(pos,n,elem); //在pos位置插入n个elem数据,无返回值。
  • list.insert(pos,beg,end); //在pos位置插入[beg,end)区间的数据,无返回值。
list<int> listA;
list<int> listB;

listA.push_back(1);
listA.push_back(2);
listA.push_back(3);
listA.push_back(4);
listA.push_back(5);

listB.push_back(11);
listB.push_back(12);
listB.push_back(13);
listB.push_back(14);

listA.insert(listA.begin(), -1); //{-1, 1, 2, 3, 4, 5}
listA.insert( ++listA.begin(), 2, -2); //{-1, -2, -2, 1, 2, 3, 4, 5}
listA.insert(listA.begin() , listB.begin() , listB.end()); //{11, 12, 13, 14, -1, -2, -2, 1, 2, 3, 4, 5}

for(list<int>::iterator it = listA.begin(); it!=listA.end(); it++)
{
    cout<< *it<<endl;
}

5.8 list的删除

  • list.clear(); //移除容器的所有数据
  • list.erase(beg,end); //删除[beg,end)区间的数据,返回下一个数据的位置。
  • list.erase(pos); //删除pos位置的数据,返回下一个数据的位置。
  • lst.remove(elem); //删除容器中所有与elem值匹配的元素。
#include <list>
#include <vector>
#include <iostream>

using namespace std;

int main(void)
{
    //list 删除元素
    list<int> listA;

    listA.push_back(1);
    listA.push_back(2);
    listA.push_back(3);
    listA.push_back(4);
    listA.push_back(5);

    //erase 的用法
    list<int>::iterator itBegin=listA.begin();
    ++ itBegin;
    list<int>::iterator itEnd=listA.begin();
    ++ itEnd;
    ++ itEnd;
    ++ itEnd;
    listA.erase(itBegin,itEnd);//此时容器lstInt包含按顺序的1, 4, 5三个元素。

    listA.erase(listA.begin());//此时容器lstInt包含按顺序的4, 5三个元素。
    listA.push_back(4); // 4, 5, 4
    listA.insert(listA.end(), 5, 4);  //4, 5, 4, 4, 4, 4, 4, 4
    /*remove 删除元素*/
    //方式一  直接调用remove 方法
    //listA.remove(4);

    //方式二   遍历然后逐个删除
    for(list<int>::iterator it=listA.begin(); it!=listA.end(); )
    {
        if(*it == 4)
        {
            it =listA.erase(it); //相当于执行了++
        }
        else
        {
            it++;
        }
    }

    for (list<int>::iterator it=listA.begin(); it!=listA.end(); ++it)
    {
        cout << *it;
        cout << " ";
    }
    
    return 0;
}

5.9 list的反序排列

  • list.reverse(); //反转链表,比如list包含1, 2, 3, 4, 5五个元素,运行此方法后,list就包含5, 4, 3, 2, 1元素。
list<int> listA;

listA.push_back(1);
listA.push_back(2);
listA.push_back(3);
listA.push_back(4);
listA.push_back(5);

listA.reverse(); //5, 4, 3, 2, 1

六 Set和multiset容器

6.1 set/multiset容器概念

set和multiset是一个集合容器,其中set所包含的元素是唯一的,集合中的元素按一定的顺序排列。set采用红黑树变体的数据结构实现,红黑树属于平衡二叉树。在插入操作和删除操作上比vector快。在n个数中查找目标数的效率是 log以2为底n

红黑树定义:是每个节点都带有颜色属性(颜色为红色或黑色)的自平衡二叉查找树,满足下列性质:
1)节点是红色或黑色;
2)根节点是黑色;
3)所有叶子节点都是黑色节点(NULL);
4)每个红色节点必须有两个黑色的子节点。(从每个叶子到根的所有路径上不能有两个连续的红色节点。)
5)从任一节点到其每个叶子的所有简单路径都包含相同数目的黑色节点。

image

Set 和 multiset 特点:
set中元素插入过程是按排序规则插入,所以不能指定插入位置。
set不可以直接存取元素。(不可以使用at.(pos)与[]操作符)。
multiset与set的区别:set支持唯一键值,每个元素值只能出现一次;而multiset中同一值可以出现多次。
不可以直接修改set或multiset容器中的元素值,因为该类容器是自动排序的。如果希望修改一个元素值,必须先删除原有的元素,再插入新的元素
头文件 #include <set>

#include <set>
#include <iostream>

using namespace std;

int main()
{
    set<int> setInt;
    multiset<int> msetInt;

    // 给setInt中插入元素
    for (int i = 0; i < 10; i++)
    {
        setInt.insert(30 - i);
    }
    setInt.insert(25);
    setInt.insert(25);
    setInt.insert(25);

    // 给msetInt中插入元素
    for (int i = 0; i < 10; i++)
    {
        msetInt.insert(30 - i);
    }
    msetInt.insert(25);
    msetInt.insert(25);
    msetInt.insert(25);

    // 输出setInt中所有元素
    cout << "setInt中所有元素:";
    for (set<int>::iterator it = setInt.begin(); it != setInt.end(); it++)
    {
        cout << *it << " ";
    }
    cout << endl;

    // 输出msetInt中所有元素
    cout << "setInt中所有元素:";
    for (multiset<int>::iterator it = msetInt.begin(); it != msetInt.end(); it++)
    {
        cout << *it << " ";
    }
    cout << endl;

    return 0;
}

image

6.2 set/multiset对象构造

Set/multiset 对象的默认构造函数

set<int> setInt;                      //一个存放int的set容器。
set<float> setFloat;                  //一个存放float的set容器。
set<string> setString;                //一个存放string的set容器。
multiset<int> mulsetInt;              //一个存放int的multi set容器。
multiset<float> multisetFloat;        //一个存放float的multi set容器。
multiset<string> multisetString;      //一个存放string的multi set容器。

Set/multiset 对象的带参构造函数

set(beg,end);                    //将[beg, end)区间中的元素拷贝给本身。
set(const set &s);               //拷贝构造函数。
multiset(beg,end);               //将[beg, end)区间中的元素拷贝给本身。
multiset(const multiset &s);     //拷贝构造函数。

set对象的拷贝构造与赋值

set(const set &st);             //拷贝构造函数
set& operator=(const set &st);  //重载等号操作符
set.swap(st);                   //交换两个集合容器

示例:

set<int> setIntA;
setIntA.insert(1);
setIntA.insert(2);
setIntA.insert(3);
setIntA.insert(4);
setIntA.insert(5);

set<int> setIntB(setIntA);  //1 2 3 4 5
set<int> setIntC;
setIntC = setIntA;          //1 2 3 4 5 
setIntC.insert(6);          //1 2 3 4 5 6
setIntC.swap(setIntA);      //交换

6.3 仿函数(函数对象)functor的用法

// less 和 greater都是函数对象,又叫仿函数
// less 函数对象实现比较,为排序提供依据  从小到大排序
set<int, less<int>> setInt1;
// greater 从大到小排序
set<int, greater<int>> setInt2;

这里我们转到声明看一下

// STRUCT TEMPLATE greater
template <class _Ty = void>
struct greater {
    _CXX17_DEPRECATE_ADAPTOR_TYPEDEFS typedef _Ty _FIRST_ARGUMENT_TYPE_NAME;
    _CXX17_DEPRECATE_ADAPTOR_TYPEDEFS typedef _Ty _SECOND_ARGUMENT_TYPE_NAME;
    _CXX17_DEPRECATE_ADAPTOR_TYPEDEFS typedef bool _RESULT_TYPE_NAME;

    _NODISCARD constexpr bool operator()(const _Ty& _Left, const _Ty& _Right) const {
        return _Left > _Right;
    }
};

// STRUCT TEMPLATE less
template <class _Ty = void>
struct less {
    _CXX17_DEPRECATE_ADAPTOR_TYPEDEFS typedef _Ty _FIRST_ARGUMENT_TYPE_NAME;
    _CXX17_DEPRECATE_ADAPTOR_TYPEDEFS typedef _Ty _SECOND_ARGUMENT_TYPE_NAME;
    _CXX17_DEPRECATE_ADAPTOR_TYPEDEFS typedef bool _RESULT_TYPE_NAME;

    _NODISCARD constexpr bool operator()(const _Ty& _Left, const _Ty& _Right) const {
        return _Left < _Right;
    }
};

用左操作数和右操作数进行比较,我们在类定义的时候可以重载以下 < 和 > 运算符

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

class Book
{
public:
    Book(string name, int price):m_name(name),m_price(price) { }
    bool operator<(const Book& right) const
    {
        return this->m_price < right.m_price;
    }
    bool operator>(const Book& right) const
    {
        return this->m_price > right.m_price;
    }
    int getPrice() const
    {
        return m_price;
    }
    string getName() const
    {
        return m_name;
    }
private:
    string m_name;
    int m_price;
};
int main()
{
    set<Book, less<Book>> setBookLess;
    set<Book, greater<Book>> setBookGreater;

    Book book1("c++从入门到放弃", 50);
    Book book2("钢铁是怎么炼成的", 30);
    Book book3("西游记", 35);
    Book book4("大话数据结构", 60);
    Book book5("八股文", 45);

    // 给setBookLess中插入数据
    setBookLess.emplace(book1);
    setBookLess.emplace(book2);
    setBookLess.emplace(book3);
    setBookLess.emplace(book4);
    setBookLess.emplace(book5);

    // 给setBookGreater中插入数据
    setBookGreater.emplace(book1);
    setBookGreater.emplace(book2);
    setBookGreater.emplace(book3);
    setBookGreater.emplace(book4);
    setBookGreater.emplace(book5);

    // 打印数据
    cout << "setBookLess中的数据" << endl;
    for (set<Book, less<Book>>::iterator it = setBookLess.begin(); it != setBookLess.end(); it++)
    {
        cout << "书名:" << it->getName() << " 价格:" << it->getPrice() << endl;
    }

    cout << endl;
    cout << "setBookGreater中的数据" << endl;
    for (set<Book, greater<Book>>::iterator it = setBookGreater.begin(); it != setBookGreater.end(); it++)
    {
        cout << "书名:" << it->getName() << " 价格:" << it->getPrice() << endl;
    }

    return 0;
}

image

仿函数概念:
尽管函数指针被广泛用于实现函数回调,但C++还提供了一个重要的实现回调函数的方法,那就是函数对象。
functor,翻译成函数对象,伪函数,它是是重载了“()”操作符的普通类对象。从语法上讲,它与普通函数行为类似。
<functional>头文件中包含的 greater<>与less<>就是函数对象。

下面举出greater<int> 和 less<int>的简易实现原理。

struct greater
{
    bool operator() (const int& iLeft, const int& iRight)
    {
        return (iLeft > iRight);
    }
}

struct less
{
    bool operator() (const int& iLeft, const int& iRight)
    {
        return (iLeft < iRight);
    }
}

set/setmulti容器就是调用函数对象的operator()方法去比较两个值的大小。

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

class Book
{
public:
    Book(string name, int price):m_name(name),m_price(price) { }
    bool operator<(const Book& right) const
    {
        return this->m_price < right.m_price;
    }
    bool operator>(const Book& right) const
    {
        return this->m_price > right.m_price;
    }
    int getPrice() const
    {
        return m_price;
    }
    string getName() const
    {
        return m_name;
    }
private:
    string m_name;
    int m_price;
};

class FunBook
{
public:
    // 类里面重载了()运算符,使类的对象可以像函数一样调用
    bool operator()(const Book& left, const Book& right) const
    {
        return left.getPrice() < right.getPrice();
    }
};

int main()
{
    Book book1("c++从入门到放弃", 50);
    Book book2("钢铁是怎么炼成的", 30);
    Book book3("西游记", 35);
    Book book4("大话数据结构", 60);
    Book book5("八股文", 45);

    // 函数对象可以像函数一样直接调用
    FunBook fBook;
    bool ret = fBook(book1, book2);
    cout << "fBook(book1, book2) 比较结果:" << ret << endl;

    // 用FunBook来实例化set对象
    set<Book, FunBook> setFunBook;
    setFunBook.emplace(book1);
    setFunBook.emplace(book2);
    setFunBook.emplace(book3);
    setFunBook.emplace(book4);
    setFunBook.emplace(book5);

    // 打印setFunBook中的所有数据
    cout << endl <<"setFunBook中的所有数据" << endl;
    for (set<Book, FunBook>::iterator it = setFunBook.begin(); it != setFunBook.end(); it++)
    {
        cout << it->getName() << " " << it->getPrice() << endl;
    }

    return 0;
}

image

6.4 set的插入和pair的用法

pair表示一个对组,它将两个值视为一个单元,把两个值捆绑在一起。
pair<T1,T2>用来存放的两个值的类型,可以不一样,也可以一样,如T1为int,T2为float。T1,T2也可以是自定义类。
pair.first是pair里面的第一个值,是T1类型。
pair.second是pair里面的第二个值,是T2类型。

当我们用emplace或insert插入元素时,转到声明看一下函数返回值

template <class... _Valtys>
pair<iterator, bool> emplace(_Valtys&&... _Vals) {
    const auto _Result = _Emplace(_STD forward<_Valtys>(_Vals)...);
    return {iterator(_Result.first, _Get_scary()), _Result.second};
}

template <bool _Multi2 = _Multi, enable_if_t<!_Multi2, int> = 0>
pair<iterator, bool> insert(value_type&& _Val) {
    const auto _Result = _Emplace(_STD move(_Val));
    return {iterator(_Result.first, _Get_scary()), _Result.second};
}

返回值都是pair<iterator, bool>类型,我们再看一下pair的声明

template <class _Ty1, class _Ty2>
struct pair { // store a pair of values
    using first_type  = _Ty1;
    using second_type = _Ty2;

示例

#include <set>
#include <iostream>
using namespace std;
int main()
{
    set<int> setInt;

    // 向setInt中插入5个元素
    for (int i = 0; i < 5; i++)
    {
        setInt.emplace(i);
    }

    // 打印当前所有元素
    cout << "当前所有元素:";
    for (set<int>::iterator it = setInt.begin(); it != setInt.end(); it++)
    {
        cout << *it << " ";
    }
    cout << endl;

    // 用emplace插入元素并判断有没有插入成功,这时可以用pair
    cout << endl << "测试用emplace插入3" << endl;
    pair<set<int>::iterator, bool> ret1 = setInt.emplace(3);
    cout << "*(ret1.first): " << *(ret1.first) << endl;  // 第一个是迭代器
    cout << "ret1.second: " << ret1.second << endl;      // 第二个是bool类型,是否插入成功 0插入失败,1插入成功

    // 用insert插入元素并判断有没有插入成功
    cout << endl << "测试用insert插入7" << endl;
    pair<set<int>::iterator, bool> ret2 = setInt.insert(7);
    cout << "*(ret2.first): " << *(ret2.first) << endl;
    cout << "ret2.second: " << ret2.second << endl;

    // 打印当前所有元素
    cout << "当前所有元素:";
    for (set<int>::iterator it = setInt.begin(); it != setInt.end(); it++)
    {
        cout << *it << " ";
    }
    cout << endl;

    return 0;
}

image

6.5 set与迭代器

set.insert(elem); //在容器中插入元素。
set.begin(); //返回容器中第一个数据的迭代器。
set.end(); //返回容器中最后一个数据之后的迭代器。
set.rbegin(); //返回容器中倒数第一个元素的迭代器。
set.rend(); //返回容器中倒数最后一个元素的后面的迭代器。

示例:

set<int> setInt;
setInt.insert(3); 
setInt.insert(4);
setInt.insert(1);
setInt.insert(5);
setInt.insert(2);

//顺序输出  1 2 3 4 5
for(set<int>::iterator it=setInt.begin(); it!=setInt.end(); ++it)
{
    cout << *it; 
}

set.rbegin()与set.rend()。略。

6.6 set/multiset的大小

set.size(); //返回容器中元素的数目
set.empty(); //判断容器是否为空
set.max_size(); //返回集合的最大长度
注意事项: 它们没有resize方法

set<int> setIntA;
setIntA.insert(3);
setIntA.insert(1);
setIntA.insert(7);
setIntA.insert(5);
setIntA.insert(9);

if (!setIntA.empty())
{
    int iSize = setIntA.size(); //5
}

6.7 set/multiset的删除

set.clear(); //清除所有元素
set.erase(pos); //删除pos迭代器所指的元素,返回下一个元素的迭代器。
set.erase(beg,end); //删除区间[beg,end)的所有元素,返回下一个元素的迭代器。
set.erase(elem); //删除容器中值为elem的元素。

删除区间内的某个或某些元素
setInt是用set<int>声明的容器,假设它内部现已包含按顺序的1, 2, 3, 4, 5, 6元素。

set<int>::iterator itBegin=setInt.begin();
++ itBegin; 
set<int>::iterator itEnd=setInt.begin();
++ itEnd;
++ itEnd;
++ itEnd;
setInt.erase(itBegin,itEnd);
//此时容器setInt包含按顺序的1, 4, 5, 6四个元素。

// 删除容器中第一个元素
setInt.erase(setInt.begin()); //4, 5, 6
// 删除容器中值为5的元素
setInt.erase(5);               //4, 6
// 删除setInt的所有元素
setInt.clear(); //容器为空

6.8 set/multiset的查找

set.find(elem); //查找elem元素,返回指向elem元素的迭代器。
set.count(elem); //返回容器中值为elem的元素个数。对set来说,要么是0,要么是1。对multiset来说,值可能大于1。
set.lower_bound(elem); //返回第一个>=elem元素的迭代器。
set.upper_bound(elem); //返回第一个>elem元素的迭代器。
set.equal_range(elem); //返回容器中与elem相等的上下限的两个迭代器。上限是闭区间,下限是开区间,如[beg,end)。以上函数返回两个迭代器,而这两个迭代器被封装在pair中。

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

int main()
{
    set<int> setInt;
    setInt.emplace(15);
    setInt.emplace(19);
    setInt.emplace(13);
    setInt.emplace(18);
    setInt.emplace(20);

    // 打印所有元素
    cout << "当前集合所有元素:";
    for (set<int>::iterator it = setInt.begin(); it != setInt.end(); ++it)
    {
        cout << *it << " ";
    }
    cout << endl;

    // 用find查找元素的时候,可以结合end来确定元素是否存在
    cout << endl <<"test find" << endl;
    set<int>::iterator it = setInt.find(17);
    if (it != setInt.end())
    {
        cout << "找到要查找的元素:" << *it << endl;
    }
    else
    {
        cout << "setInt.find(17) 该元素不存在" << endl;
    }

    // count返回容器中某个元素的个数
    cout << endl << "test count" << endl;
    cout << "setInt.count(17) = " << setInt.count(17) << endl;

    // 测试lower_bound和upper_bound
    cout << endl << "test lower_bound 和 upper_bound" << endl;
    set<int>::iterator it1 = setInt.lower_bound(13);     // 返回第一个 >= 17的迭代器
    set<int>::iterator it2 = setInt.upper_bound(13);     // 返回第一个 > 13 的迭代器
    cout << "setInt.lower_bound(13):" << *it1 << endl;   // 13
    cout << "setInt.upper_bound(13):" << *it2 << endl;   // 15

    // 测试equal_range
    cout << endl << "test equal_range" << endl;
    pair<set<int>::iterator, set<int>::iterator> paitIt = setInt.equal_range(13);
    cout << "setInt.equal_range(13) *(paitIt.first):" << *(paitIt.first) << endl;
    cout << "setInt.equal_range(13) *(paitIt.second):" << *(paitIt.second) << endl;

    return 0;
}

image

七 Map和multimap容器

7.1 map/multimap的概念

map是标准的关联式容器,一个map里存储的元素是一个键值对序列,叫做(key,value)键值对。它提供基于key快速检索数据的能力。

map中key值是唯一的。集合中的元素按一定的顺序排列。元素插入过程是按排序规则插入,所以不能指定插入位置。
map底层的具体实现是采用红黑树变体的平衡二叉树的数据结构。在插入操作、删除和检索操作上比vector快很多。
map可以直接存取key所对应的value,支持[]操作符,如map[key]=value。
头文件#include <map>

multimap与map的区别:
map支持唯一键值,每个键只能出现一次;而multimap中相同键可以出现多次。multimap不支持[]操作符。

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

using namespace std;

int main()
{
    map<int, string> mapStu;
    mapStu.emplace(pair<int, string>(18, "小明"));
    mapStu.emplace(pair<int, string>(16, "小红"));
    mapStu.insert(pair<int, string>(20, "小张"));
    
    // 打印所有数据
    cout << "打印当前所有数据" << endl;
    for (map<int, string>::iterator it = mapStu.begin(); it != mapStu.end(); it++)
    {
        cout << "key:" << (*it).first << " 姓名:" << (*it).second << endl;
    }
    cout << endl;

    // map可以直接存取key所对应value
    mapStu[14] = "小王";    // 将key为14的value的值设为"小王"
    mapStu[20] = "小李";
    cout << "执行mapStu[14] = \"小王\"; mapStu[20] = \"小李\";之后的所有数据" << endl;
    for (map<int, string>::iterator it = mapStu.begin(); it != mapStu.end(); it++)
    {
        cout << "key:" << (*it).first << " 姓名:" << (*it).second << endl;
    }

    return 0;
}

image

7.2 map/multimap对象的构造

map/multimap对象的默认构造

//map/multimap采用模板类实现,对象的默认构造形式:
map<T1,T2> mapTT; 
multimap<T1,T2>  multimapTT;  
//如:
map<int, char> mapA;
map<string,float> mapB;
//其中T1,T2还可以用各种指针类型或自定义类型

map和multimap对象的带参数构造

map(beg,end);     //将[beg, end)区间中的元素拷贝给本身。
map(const map &mapObject); //拷贝构造函数。

7.3 map的插入与迭代器

map.insert(...); //往容器插入元素,返回pair<iterator,bool>

map中插入元素的四种方式:
假设 map<int, string> mapStu;

//方式一、通过pair的方式插入对象
mapStu.insert(  pair<int,string>(1,"张三")  );
//方式二、通过pair的方式插入对象
mapStu.inset(make_pair(2, “李四”));
//方式三、通过value_type的方式插入对象
mapStu.insert(  map<int,string>::value_type(3,"王五")  );
//方式四、通过数组的方式插入值
mapStu[4] = "赵六";
mapStu[5] = “小七";

注意:
前三种方法,采用的是insert()方法,该方法返回值为pair<iterator,bool>, 可以用来判断插入是否成功

int main()
{
    map<int, string> mapStu;
    mapStu.emplace(pair<int, string>(18, "小明"));
    mapStu.emplace(pair<int, string>(16, "小红"));
    mapStu.insert(pair<int, string>(20, "小张"));

    // 打印所有数据
    cout << "打印当前所有数据" << endl;
    for (map<int, string>::iterator it = mapStu.begin(); it != mapStu.end(); it++)
    {
        cout << "key:" << (*it).first << " 姓名:" << (*it).second << endl;
    }
    cout << endl;

    // 判断是否插入成功
    pair<map<int, string>::iterator, bool> ret = mapStu.emplace(pair<int, string>(22, "李四"));  // 如果这里key是已经存在的18,那么插入失败
    if (ret.second)
    {
        cout << (*(ret.first)).first << "key插入成功, 值为" << (*(ret.first)).second << endl;
    }
    else
    {
        cout << "插入失败" << endl;
    }

    return 0;
}

image
image

第四种方法非常直观,但碰到相同的键时会进行覆盖操作。比如插入key 为4的键值时,先在mapStu中查找主键为4的项,若不存在,则将一个键为4,值为默认初始化值的对组插入到mapStu中,然后再将值修改成“赵六”。若发现已存在4这个键,则修改这个键对应的value。

string strName = mapStu[8];   //取值操作或插入操作
//只有当mapStu存在8这个键时才是正确的取操作,否则会自动插入一个实例,键为8,值为默认构造时的初始化值。

迭代器:
map.begin(); //返回容器中第一个数据的迭代器。
map.end(); //返回容器中最后一个数据之后的迭代器。
map.rbegin(); //返回容器中倒数第一个元素的迭代器。
map.rend(); //返回容器中倒数最后一个元素的后面的迭代器。

7.4 map/multimap 排序

map<T1,T2,less<T1> > mapA; //该容器是按键的升序方式排列元素。未指定函数对象,默认采用less<T1>函数对象。
map<T1,T2,greater<T1> > mapB; //该容器是按键的降序方式排列元素。
less<T1>与greater<T1> 可以替换成其它的函数对象functor。
可编写自定义函数对象以进行自定义类型的比较,使用方法与set构造时所用的函数对象一样。

7.5 map对象的拷贝构造与赋值

map(const map &mp); //拷贝构造函数
map& operator=(const map &mp); //重载等号操作符
map.swap(mp); //交换两个集合容器

map<int, string> mapA;
mapA.insert(pair<int,string>(2, "李四"));	
mapA.insert(pair<int,string>(1, "张三"));	
mapA.insert(pair<int,string>(3, "王五"));	
mapA.insert(pair<int,string>(4, "赵六"));	

map<int ,string> mapB(mapA); //拷贝构造,此时mapB 和mapA中元素一致
map<int, string> mapC;
mapC = mapA;  //赋值,此时mapC 和mapA中元素一致
mapC[3] = "老张"; //mapC中,把王五修改为老张,此时包含 张三, 李四, 老张, 赵六
mapC.swap(mapA); //mapA 和mapC交换

7.6 map的大小

map.size(); //返回容器中元素的数目
map.empty();//判断容器是否为空
注意:没有resize方法

map<int, string> mapA;
mapA.insert(pair<int,string>(2, "李四"));
mapA.insert(pair<int,string>(1, "张三"));
mapA.insert(pair<int,string>(3, "王五"));
mapA.insert(pair<int,string>(4, "赵六"));
if (!mapA.empty())
{
    int size = mapA.size(); //size 为 4
}

7.7 map的删除

map.clear(); //删除所有元素
map.erase(pos); //删除pos迭代器所指的元素,返回下一个元素的迭代器。
map.erase(beg,end); //删除区间[beg,end)的所有元素,返回下一个元素的迭代器。
map.erase(key); //删除容器中key为key的对组,返回删除的对组个数
Map.erase(key_type *first, key_type *last) //删除数组指定的半闭半开的区间中特定的key对应的所有队组

map<int, string> mapA;
mapA.insert(pair<int,string>(2, "李四"));	  
mapA.insert(pair<int,string>(1, "张三"));	
mapA.insert(pair<int,string>(3, "王五"));	
mapA.insert(pair<int,string>(4, "赵六"));	

//删除区间内的元素,迭代器指示区间(半闭半开)
map<int,string>::iterator itBegin=mapA.begin();
++ itBegin;
map<int,string>::iterator itEnd=mapA.end();
mapA.erase(itBegin,itEnd);    //此时容器mapA仅仅包含{1,"张三"}一个元素。

mapA.insert(pair<int,string>(2, "李四"));
mapA.insert(pair<int,string>(3, "王五"));
mapA.insert(pair<int,string>(4, "赵六"));

//删除容器中的第一个元素,使用迭代器指示位置
mapA.erase(mapA.begin());	//mapA包含{2,"李四"}{3,"王五"}{4,"赵六"}三个元素
//删除容器中key为4的元素
mapA.erase(4);    
//删除mapA的所有元素
mapA.clear();//容器为空

7.8 map/multimap的查找

map.find(key); 查找键key是否存在,若存在,返回该键的元素的迭代器;若不存在,返回map.end();
map.count(key); //返回容器中键值为key的对组个数。对map来说,要么是0,要么是1;对multimap来说,值>=0。
map.lower_bound(keyElem); //返回第一个key >= keyElem元素的迭代器。
map.upper_bound(keyElem); // 返回第一个key > keyElem元素的迭代器。
map.equal_range(keyElem); //返回容器中key与keyElem相等的上下限的两个迭代器。上限是闭区间,下限是开区间,如[beg,end)。

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

using namespace std;

int main()
{
    map<int, string> mapStu;
    multimap<int, string> mapTeacher;

    mapStu.emplace(pair<int, string>(2, "李四"));
    mapStu.emplace(make_pair(1, "张三"));
    mapStu.emplace(pair<int, string>(4, "赵六"));
    mapStu.emplace(map<int, string>::value_type(3, "王五"));

    mapTeacher.emplace(make_pair(101, "张老师"));
    mapTeacher.emplace(make_pair(102, "李老师"));
    mapTeacher.emplace(make_pair(101, "王老师"));
    mapTeacher.emplace(make_pair(101, "郭老师"));
    mapTeacher.emplace(make_pair(103, "赵老师"));

    cout << "当前所有学生数据" << endl;
    for (map<int, string>::iterator it = mapStu.begin(); it != mapStu.end(); it++)
    {
        cout << "key:" << (*it).first << " name:" << (*it).second << endl;
    }
    cout << endl;

    cout << "当前所有老师数据" << endl;
    for (multimap<int, string>::iterator it = mapTeacher.begin(); it != mapTeacher.end(); it++)
    {
        cout << "key:" << (*it).first << " name:" << (*it).second << endl;
    }
    cout << endl;

    // map的查找
    cout << "test map的find" << endl;
    map<int, string>::iterator isFind = mapStu.find(2);
    if (isFind != mapStu.end())
    {
        //找到元素
        cout << "找到数据 key:" << (*isFind).first << " name:" << (*isFind).second << endl;
    }
    else
    {
        cout << "没有找到数据" << endl;
    }
    cout << endl;

    // multimap的查找
    cout << "test multimap的find" << endl;
    int findKey = 101;
    multimap<int, string>::iterator mit = mapTeacher.find(findKey);
    if (mit != mapTeacher.end())
    {
        // 找到数据  输出multimap中同一键的多个值
        // 方法1:通过比较key,循环判断  推荐使用方法1
        while ((*mit).first == findKey)
        {
            cout << "key:" << (*mit).first << " name:" << (*mit).second << endl;
            mit++;
        }

        // 方法2:通过count计数来控制
        //int nCount = mapTeacher.count(findKey);
        //for (int i = 0; i < nCount; i++)
        //{
        //    cout << "key:" << (*mit).first << " name:" << (*mit).second << endl;
        //    mit++;
        //}
    }
    else
    {
        cout << "没有找到数据" << endl;
    }
    cout << endl;

    // equal_range用法
    cout << "test equal_range" << endl;
    int value = 103;
    pair<multimap<int,string>::iterator, multimap<int, string>::iterator> mmiit = mapTeacher.equal_range(value);
    if ((mmiit.first) != mapTeacher.end())
    {
        // 找到数据
        cout << "第一个迭代器中的key:" << (*(mmiit.first)).first << " name:" << (*(mmiit.first)).second << endl;
        if (mmiit.second != mapTeacher.end())
        {
            cout << "第二个迭代器中的key:" << (*(mmiit.second)).first << " name:" << (*(mmiit.second)).second << endl;
        }
        else
        {
            cout << "第二个迭代器指向map的end" << endl;
        }
    }
    else
    {
        cout << "不存在" << endl;
    }

    return 0;
}

image

八 queue容器 队列 先进先出

queue是队列容器,是一种先进先出的容器。
默认情况下queue是利用deque容器实现的一种容器。
它只允许在队列的前端(front)进行删除操作,而在队列的后端(back)进行插入操作
#include <queue>

8.1 queue对象的构造

queue采用模板类实现,queue对象的默认构造形式:queue<T> queT; 如:

queue<int> queueInt;             //一个存放int的queue容器。
queue<float> queueFloat;     //一个存放float的queue容器。
queue<string> queueString;     //一个存放string的queue容器。 

注意: 尖括号内还可以设置指针类型或自定义类型。

queue 对象的带参构造

queue<int, list<int>> queueList;  //内部使用list 来存储队列元素的queue 容器.
\\错误: queue<int, vector<int>> queueVector; //内部不能使用vector来存储队列元素

8.2 queue的push()与pop()方法

queue.push(elem); //往队尾添加元素
queue.pop(); //从队头处移除队首元素

queue<int> queueInt;
queueInt.push(1);
queueInt.push(2);
queueInt.push(3);
queueInt.push(4);
queueInt.pop();
queueInt.pop();
//此时queueInt存放的元素是3, 4

8.3 queue对象的拷贝构造与赋值

queue(const queue &que); //拷贝构造函数
queue& operator=(const queue &que); //重载等号操作符

queue<int> queIntA;
queIntA.push(1);
queIntA.push(2);
queIntA.push(3);
queIntA.push(4);
queIntA.push(5);

queue<int> queIntB(queIntA); //拷贝构造
queue<int> queIntC;
queIntC = queIntA; //赋值

8.4 queue的数据存取

queue.back(); //返回最后一个元素
queue.front(); //返回第一个元素

queue<int> queIntA;
queIntA.push(1);
queIntA.push(2);
queIntA.push(3);
queIntA.push(4);
queIntA.push(5);

int iFront = queIntA.front(); //1
int iBack = queIntA.back(); //5

queIntA.front() = 66; //66
queIntA.back() = 88; //88

8.5 queue的大小

queue.empty(); //判断队列是否为空
queue.size(); //返回队列的大小

queue<int> queIntA;
queIntA.push(1);
queIntA.push(2);
queIntA.push(3);
queIntA.push(4);
queIntA.push(5);

if (!queIntA.empty())
{
    int iSize = queIntA.size(); //iSize = 5
}

九 priority_queue 优先级队列

优先队列: 它的入队顺序没有变化,但是出队的顺序是根据优先级的高低来决定的。优先级高的优先出队。

最大值优先级队列、最小值优先级队列, 用来开发一些特殊的应用
#include <queue>

#include <queue>
#include <list>
#include <deque>
#include <iostream>

using namespace std;

int main()
{
    priority_queue<int> pqA;  // 默认情况下是值越大,优先级越大  // 5 5 5 4 3 2 1
    //priority_queue<int, vector<int>, less<int>> pqA; // 5 5 5 4 3 2 1 
    //priority_queue<int, vector<int>, greater<int>> pqA;   // 使用vector 值越小,优先级越大 1 2 3 4 5 5 5
    //priority_queue<int, deque<int>, greater<int>> pqA;    // 使用vector 值越小,优先级越大 1 2 3 4 5 5 5
    //priority_queue<int, list<int>, greater<int>> pqA;     // 报错,不能使用list,不兼容
    pqA.emplace(1);
    pqA.emplace(2);
    pqA.emplace(5);
    pqA.emplace(3);
    pqA.emplace(5);
    pqA.emplace(4);
    pqA.emplace(5);

    while (!pqA.empty())
    {
        cout << pqA.top() << " ";
        pqA.pop();
    }
    cout << endl;

    return 0;
}

image

十 stack容器 栈 先进后出

stack是堆栈容器,是一种先进后出的容器。
stack是基于deque容器而实现的容器。
#include <stack>

10.1 stack对象的构造

stack采用模板类实现, stack对象的默认构造形式: stack <T> stkT;

stack <int> stkInt;            //一个存放int的stack容器。
stack <float> stkFloat;     //一个存放float的stack容器。
stack <string> stkString;     //一个存放string的stack容器。
//尖括号内还可以设置指针类型或自定义类型。

10.2 stack的push()与pop()方法

stack.push(elem); //往栈头添加元素
stack.pop(); //从栈头移除第一个元素

stack<int> stkInt;  
//stack<int, vector<int>> stkInt;
//stack<int, list<int>> stkInt;
//stack<int, queue<int>> stkInt;  // 报错,不能使用queue
stkInt.push(1);
stkInt.push(2);
stkInt.pop();   
stkInt.push(3);
//此时stkInt存放的元素是1, 3

10.3 stack对象的拷贝构造与赋值

stack(const stack &stk); //拷贝构造函数
stack& operator=(const stack &stk); //重载等号操作符

stack<int> stkIntA;
stkIntA.push(1);
stkIntA.push(2);
stkIntA.push(3);

stack<int> stkIntB(stkIntA) //拷贝构造
stack<int> stkIntC;
stkIntC = stkIntA; //赋值

10.4 stack的数据存取

stack.top(); //返回最后一个压入栈元素

stack<int> stkIntA;
stkIntA.push(1);
stkIntA.push(2);
stkIntA.push(3);

int iTop = stkIntA.top(); //3
stkIntA.top() = 88; //88

10.5 stack的大小

stack.empty(); //判断堆栈是否为空
stack.size(); //返回堆栈的大小

stack<int> stkInt;
stkInt.push(1);
stkInt.push(2);
stkInt.push(3);

int iSize = stkInt.size(); //3

十一 Array容器(C++11 新增)

11.1 array容器概念

array 容器是 C++ 11 标准中新增的序列容器,简单地理解,它就是在 C++ 普通数组的基础上,添加了一些成员函数和全局函数。
array是将元素置于一个固定数组中加以管理的容器。
array可以随机存取元素,支持索引值直接存取, 用[]操作符或at()方法对元素进行操作,也可以使用迭代器访问
不支持动态的新增删除操作
array可以完全替代C语言中的数组,使操作数组元素更加安全!
#include <array>

array特点:
array 容器的大小是固定的,无法动态的扩展或收缩,这也就意味着,在使用该容器的过程无法增加或移除元素而改变其大小,它只允许访问或者替换存储的元素。
STL 还提供有可动态扩展或收缩存储空间的 vector 容器

11.2 array对象的构造

array采用模板类实现,array对象的默认构造形式
array<T,int> arrayT; //T为存储的类型, 为数值型模板参数

//构造函数
array<int, 5> a1;     //一个存放5个int的array容器
array<float, 6> a2;   //一个存放6个float的array容器
array<student, 7> a3; //一个存放7个student的array容器

11.3 array的赋值

array 的赋值

a1.assign(0); //玩法一 改变array中所有元素(注:将被废弃,不推荐使用)
a1.fill(666); //玩法二 用特定值填充array中所有元素
array<int, 4> test={1, 2, 3, 4};// 玩法三 定义时使用初始化列表

array<int, 4> test;
test={1,2,3,4};   //玩法四 定义后使用列表重新赋值
array<int, 4> a1,a2;
a1={1,2,3,4};
a2 = a1;//玩法五,赋值运算

a1.swap(a2);  //玩法六  和其它array进行交换

11.4 array的大小

array.size(); //返回容器中元素的个数
array.empty(); //判断容器是否为空,逗你玩的,永远为 false
array.max_size(); //返回容器中最大元素的个数,同size()。

11.5 array的数据存取

第一  使用下标操作 a1[0] = 100;
第二  使用at 方法 如: a1.at(2) = 100;
第三  接口返回的引用 a1.front() 和 a1.back()  
注意:   第一和第二种方式必须注意越界

11.6 array 迭代器访问

array.begin(); //返回容器中第一个数据的迭代器。
array.end(); //返回容器中最后一个数据之后的迭代器。
array.rbegin(); //返回容器中倒数第一个元素的迭代器。
array.rend(); //返回容器中倒数最后一个元素的后面的迭代器。
array.cbegin(); //返回容器中第一个数据的常量迭代器。
array.cend(); //返回容器中最后一个数据之后的常量迭代器。
array.crbegin(); //返回容器中倒数第一个元素的常量迭代器。
array.crend(); //返回容器中倒数最后一个元素的后面的常量迭代器。

array<int, 5> arrayInt = {1, 2, 3, 4, 5};

//顺序输出  1 2 3 4 5
for(array<int, 5>::iterator it=arrayInt .begin(); it!=arrayInt .end(); ++it)
{
      int elem = *it;
      cout << elem;    //或直接使用cout << *it
}

set.rbegin()与set.rend()。略。

十二 STL常见疑难杂症

注意:任何时候在模板(template)中使用一个嵌套从属类型名称, 需要在前一个位置, 添加关键字typename;
比如以下示例中使用迭代器类型时,就要使用typename.虽然在vs2010 和vs2015中没有错误,但在VC++2019和gcc编译器中,都会报错。

#include <iostream>
#include <deque>
#include <string>
#include <vector>
#include <list>
#include <Windows.h>

using namespace std;

template <typename T>
void printInf(const list<T>& object) throw()
{
    string line(50, '-');
    typename list<T>::const_iterator citor;   // 注意这里要加typename
    for (citor = object.begin(); citor != object.end(); citor++) {
        cout << *citor << endl;
    }
    cout << endl;
    cout << "size:" << object.size() << endl;
    cout << line << endl;
    return;
}

class Student
{
public:
    Student() {
        cout << "默认构造函数" << endl;
        this->m_nAge = 0;
        this->m_sName = "未知";
    }
    Student(int _age, const char* _name) {
        cout << "带参数的构造函数" << endl;
        this->m_nAge = _age;
        this->m_sName = _name;
    }
    Student(const Student& object) {
        cout << "拷贝构造函数" << endl;
        this->m_nAge = object.m_nAge;
        this->m_sName = object.m_sName;
    }
    ~Student() {
        cout << "析构函数 " << endl;
    }
    friend ostream& operator<<(ostream& out, const Student& stu);
public:
    string m_sName;
    int m_nAge;
};

ostream& operator<<(ostream& out, const Student& stu) {
    out << "年龄:" << stu.m_nAge << "\t" << "姓名:" << stu.m_sName;
    return out;
}

int main(int agrc, char** argv)
{
    Student s1(21, "张大帅");
    Student s2(21, "李小美");
    Student s3(51, "张三");
    Student s4(50, "罗二");

    list<Student> stuList;

    printInf<Student>(stuList);

    system("pause");
    return 0;
}

posted @ 2022-05-19 01:26  荒年、  阅读(237)  评论(0编辑  收藏  举报