C++STL容器学习笔记

初识STL

C++的面向对象和泛型编程思想,目的就是提高代码的复用性,为建立数据结构和算法的一套标准,诞生了STL。

STL基本概念

STL(Standard Template Library)标准模板库。

  • STL从广义上分为:容器、算法、迭代器
  • 容器和算法之间通过迭代器进行无缝连接
  • STL几乎所有的代码都采用了模板类或者模板函数

STL六大组件

STL六大组件:容器、算法、迭代器、仿函数、适配器(配接器)、空间配置器

  1. 容器:各种数据结构,如vector、list、deque、set、map等,用来存放数据
  2. 算法:各种常用的算法,如sort、find、copy、for_each等
  3. 迭代器:扮演了容器与算法之间的胶合剂
  4. 仿函数:行为类似函数,可作为算法的某种策略
  5. 适配器:一种用来修饰容器或者仿函数或迭代器接口的东西
  6. 空间配置器:负责空间的配置与管理

STL中容器、算法、迭代器

  • 容器:存放数据。
    常用的数据结构:数组、链表、树、栈、队列、集合等
    这些容器又可分为序列式容器关联式容器两种:
    序列式容器:强调值的排序,序列式容器中的每个元素均有固定的位置。
    关联式容器:二叉树结构,各元素之间没有严格的物理上的顺序关系。

  • 算法:分为质变算法非质变算法。有限的步骤,解决逻辑或数学上的问题,这一门学科称之为算法。
    质变算法:运算过程中会改变区间内元素的值,如拷贝、替换、删除。
    非质变算法:运算过程中不会改变区间内的元素内容,例如查找、计数、遍历、寻找极值。

  • 迭代器:提高一种方法,使之能够依次寻访某个容器所含的各个元素,而又无需暴漏该容器的内部表示方式。
    每个容器都有自己专属的迭代器,迭代器的使用非常类似于指针
    迭代器种类:
    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-o546zOzF-1685616730341)(images/4.png)]
    常用的容器中迭代器种类为双向迭代器,和随机访问迭代器

Vector(向量)容器

STL中最常用的容器为Vector,可以理解为数组。
STL中每个容器在使用前都需要包含头文件。
vector存放内置数据类型:

  • 容器:vector
  • 算法:for_each
  • 迭代器:vector::iterator
    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-JI4VSTn0-1685616730343)(images/5.png)]

vector存放内置数据类型

#include 
#include 
#include  // STL标准算法头文件,使用for_each时需要
using namespace std;

void myPrint(int val)
{
    cout << val << endl;
}

void test()
{
    vector<int> v; // 创建一个容器

    // 向容器中插入数据(尾插)
    v.push_back(1);
    v.push_back(2);
    v.push_back(3);

    // 通过迭代器访问容器中的数据
    // vector::iterator itBegin = v.begin(); // 起始迭代器,指向容器中第一个元素的位置
    // vector::iterator itEnd = v.end();     // 结束迭代器,指向容器中最后一个元素的下一个位置

    // // 遍历方式1
    // while (itBegin != itEnd)
    // {
    //     cout << *itBegin << endl;
    //     itBegin++;
    // }

    // 遍历方式2:更常用
    for (vector<int>::iterator it = v.begin(); it != v.end(); it++)
    {
        cout << *it << endl;
    }

    // 遍历方式3:利用STL提供的遍历算法
    // for_each(v.begin(), v.end(), myPrint); // 需要定义一个打印输出的函数,传入函数名
}

int main()
{
    test();
    return 0;
}

vector中存放自定义数据类型,并打印输出

#include 
#include 
#include 
using namespace std;

// vector容器中存放自定义数据类型
class Person
{
public:
    string m_name;
    int m_age;
    Person(string name, int age)
    {
        this->m_name = name;
        this->m_age = age;
    }
};

void test1()
{
    vector<Person> v; // 创建一个存放自定义数据类型class的容器
    Person p1("张三", 1);
    Person p2("李四", 2);

    // 向容器中添加数据
    v.push_back(p1);
    v.push_back(p2);

    // 遍历容器中的数据
    for (vector<Person>::iterator it = v.begin(); it != v.end(); it++)
    {
        // (*it)的数据类型为Person
        cout << "name:" << (*it).m_name << ",age:" << (*it).m_age << endl;
    }
}

// 存放自定义数据类型:指针
void test2()
{
    vector<Person *> v;

    Person p1("张三", 1);
    Person p2("李四", 2);

    v.push_back(&p1);
    v.push_back(&p2);

    // 遍历容器
    for (vector<Person *>::iterator it = v.begin(); it != v.end(); it++)
    {
        cout << "name:" << (*it)->m_name << endl;
    }
}

int main()
{
    test1();
    test2();
    return 0;
}

vector容器嵌套容器

学习目标:容器中嵌套容器,将所有数据进行遍历输出。
容器嵌套相当于一个二维数组。

#include 
#include 
using namespace std;

// 容器嵌套输出
void test1()
{
    vector<vector<int>> v;

    // 创建小容器
    vector<int> v1;
    vector<int> v2;
    vector<int> v3;

    // 向小容器中添加数据
    for (int i = 0; i < 3; i++)
    {
        v1.push_back(i + 1);
        v2.push_back(i + 2);
        v3.push_back(i + 3);
    }

    // 将小容器插入到大容器中
    v.push_back(v1);
    v.push_back(v2);
    v.push_back(v3);

    // 通过大容器,把所有数据遍历一遍,与二维数组的遍历方式一样
    for (vector<vector<int>>::iterator it = v.begin(); it != v.end(); it++)
    {
        //  (*it) = vector;  技巧:(*it)等于尖括号里的内容
        // 遍历小容器
        for (vector<int>::iterator vit = (*it).begin(); vit != (*it).end(); vit++)
        {
            cout << *vit << " "; // 输出小容器中的数据
        }
        cout << endl;
    }
}

int main()
{
    test1();
    return 0;
}

string基本概念

  • 本质:string是C++风格的字符串,string本质上是一个类
    string和char *的区别:
  • char *是一个指针
  • string是一个类,类内部封装了char *,是一个char *容器
    特点:
  1. string类内部封装了很多成员方法:find查找,delete删除、replace删除、insert插入
  2. string管理char *所分配的内存,不用担心复制越界和取值越界的问题,由类内部进行负责

string构造函数

构造函数原型:

  • string();创建一个空的字符串,例如string str1;
  • string(const char *s);使用字符串s初始化
  • string(const string& str);使用一个string对象初始化另一个string对象
  • string(int n, char c)使用n个字符c初始化
#include 
#include 
using namespace std;

void test1()
{
    string s1; // 默认构造
    const char *str = "hello,world";
    string s2(str);
    cout << s2 << endl; // 输出hello,world
}

int main()
{
    test1();
    return 0;
}

string赋值操作

#include 
#include 
using namespace std;

void test1()
{
    string str1 = "hello,world";

    string str2;
    str2.assign("hello,c++"); // 使用string类的成员函数进行赋值

    string str3;
    str3.assign("hello,c++", 5); // 把字符串中的前5个字符赋值给str3
    cout << str3 << endl;        // 输出hello

    string str4;
    str4.assign(str3); // 拷贝赋值,利用另外一个字符串,给str3赋值

    string str5;
    str5.assign(10, 'w'); // 输出10个w
    cout << str5 << endl;
}

int main()
{
    test1();
    return 0;
}

字符串拼接

功能描述:实现在字符串末尾拼接字符串

#include 
#include 
using namespace std;

void test1()
{
    string str1 = "hello";
    str1 += ",world"; // 追加一个字符串
    str1 += '.';      // 追加一个字符
    cout << str1 << endl;

    string str2 = "test";
    str2 += str1;
    cout << str2 << endl;

    // 追加方式2:采用string类的成员函数
    string str3 = "test2";
    str3.append(" ,"); // 追加字符串或字符

    str3.append("hello,world", 4); // 追加字符串hello,world的前4个字符
    cout << str3 << endl;

    // 参数2:从哪个位置开始截取,参数3:截取字符个数
    str3.append(str2, 0, 3); // 从字符串str2的第0个位置(起始位置)截取3个字符,追加到str3

    cout << str3 << endl;
}

int main()
{
    test1();
    return 0;
}

字符串查找和替换

功能描述:

  • 查找:查找指定字符串是否存在
  • 替换:在指定的位置替换字符串
    示例1:字符串查找find()rfind()
  • find查找是从左往右,rfind是从右往左
  • find找到字符串后返回查找到的第一个字符位置(起始位置为0),找不到返回-1
  • replace在替换时,要指定从那个位置起,多少个连续的字符,替换为str
#include 
#include 
using namespace std;

// 字符串查找
void test1()
{
    string str1 = "abcdef";
    int pos = str1.find("de"); // 默认从0号位置查找 字符串第一次出现的位置
    if (pos == -1)
    {
        cout << "未找到字符串" << endl;
    }
    cout << pos << endl; // 输出结果:3(起始位置为0)。如果没有找到,输出:-1

    // rfind 和 find的区别
    // rfind 从右往左开始查找。find从左往右开始查找
    pos = str1.rfind("a");
    cout << pos << endl;  // 输出结果0
}

int main()
{
    test1();
    return 0;
}

示例2:字符串替换replace()

#include 
#include 
using namespace std;

// 字符串查找
void test1()
{
    string str1 = "abcdef";

    // 参数1:起始位置,参数2:从pos起始位置开始把连续的3个字符,替换为字符串str
    str1.replace(1, 3, "1");
    cout << str1 << endl; // 输出结果:a1ef 。把bcd这三个字符,替换成了字符串str
}

int main()
{
    test1();
    return 0;
}

字符串比较compare()

  • 功能描述:字符串之间的比较,从起始位置开始比
  • 比较方式:按字节的ASCII码进行对比
    • =,输出0
    • >,输出1
    • <,输出-1
      总结:字符串对比主要用于比较两个字符串是否相等,判断谁大谁小的意义并不是很大。
#include 
#include 
using namespace std;

void test1()
{
    string str1 = "bcd";
    string str2 = "abcde";
    int result = str1.compare(str2);

    cout << result << endl; // 输出1
}

int main()
{
    test1();
    return 0;
}

字符串存取

功能描述:存取单个字符

#include 
#include 
using namespace std;

void test1()
{
    string str1 = "abcd";
    cout << str1.size() << endl; // 输出4,不包括字符串的结束符
    // 访问方式1:通过[]访问单个字符
    for (int i = 0; i < str1.size(); i++)
    {
        cout << str1[i] << " ";
    }
    cout << endl;

    // 访问方式2:通过at访问
    for (int i = 0; i < str1.size(); i++)
    {
        cout << str1.at(i) << " ";
    }
    cout << endl;

    // 修改单个字符
    str1[0] = 'x'; // str1.at(0) = 'x';

    cout << str1 << endl;
}

int main()
{
    test1();
    return 0;
}

字符串插入和删除

  • insert()插入字符串
  • erase()删除从pos开始的m个字符
#include 
#include 
using namespace std;

void test1()
{
    // 插入
    string str1 = "abcd";
    str1.insert(1, "123"); // 在第一个字符之后,插入字符串
    cout << str1 << endl;  // 输出:a123bcd

    // 删除
    str1.erase(1, 3);     // 从第一个位置开始,删除3个字符
    cout << str1 << endl; // 输出:abcd
}

int main()
{
    test1();
    return 0;
}

总结:插入和删除的起始下标都是从0开始。

字符串截取substr()

  • 功能描述:从字符串中获取想要的子串
#include 
#include 
using namespace std;

void test1()
{
    string str1 = "abcdefg";
    string subStr = str1.substr(1, 3); // 从1号位置,截取3个连续的字符
    cout << subStr << endl;            // 输出:bcd
}

// 实用操作
void test2()
{
    string email = "[email protected]";
    // 从邮件地址中获取用户信息
    int pos = email.find("@");
    cout << pos << endl;
    string username = email.substr(0, pos); // 输出:hello
    cout << username << endl;
}

int main()
{
    test1();
    test2();
    return 0;
}

vector容器基本概念

  • 功能:vector数据结构和数组非常相似,也称为单端数据
  • vector与普通数组区别:数组是静态空间vector可以动态扩展
    • 动态扩展:并不是在原空间之后接新空间,而是找更大的内存空间,然后把原数据拷贝到新空间,释放原空间
      [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-FZ4VbmtW-1685616752327)(images/6.png)]
  • vector容器的迭代器是支持随机访问的迭代器

vector构造函数

功能描述:创建vector容器。

#include 
#include 
using namespace std;

// 打印输出容器中的内容
void printVector(vector<int> &v)
{
    for (vector<int>::iterator it = v.begin(); it != v.end(); it++)
    {
        cout << *it << " ";
    }
    cout << endl;
}

void test1()
{
    vector<int> v1; // 默认构造,无参构造
    for (int i = 0; i < 10; i++)
    {
        v1.push_back(i);
    }
    printVector(v1);

    // 通过区间方式进行构造
    vector<int> v2(v1.begin(), v1.end());
    printVector(v2);

    // n个元素方式构造
    // 参数1:个数,参数2:值
    vector<int> v3(10, 100); // 容器中有10个元素,初始值为100
    printVector(v3);

    // 拷贝构造
    vector<int> v4(v3);
    printVector(v4);
}

int main()
{
    test1();
    return 0;
}

vector赋值操作

  • 功能描述:给vector容器进行赋值
#include 
#include 
using namespace std;

// 打印输出容器中的内容
void printVector(vector<int> &v)
{
    for (vector<int>::iterator it = v.begin(); it != v.end(); it++)
    {
        cout << *it << " ";
    }
    cout << endl;
}

void test1()
{
    vector<int> v1;
    for (int i = 0; i < 10; i++)
    {
        v1.push_back(i);
    }
    printVector(v1);

    // 赋值方式1
    vector<int> v2;
    v2 = v1;
    printVector(v2);

    // 赋值方式2
    vector<int> v3;
    v3.assign(v1.begin(), v1.end());
    printVector(v3);

    // 赋值方式3
    vector<int> v4;
    v4.assign(10, 100);
    printVector(v4);
}

int main()
{
    test1();
    return 0;
}

vector容量和大小

  • empty()判断容器是否为空
  • capacity()容器的容量
  • size()返回容器中元素的个数
  • resize(int num)重新指定容器的长度
  • resize(int num,elem)重新指定容器的长度,并以elem值填充新位置
#include 
#include 
using namespace std;

// 打印输出容器中的内容
void printVector(vector<int> &v)
{
    for (vector<int>::iterator it = v.begin(); it != v.end(); it++)
    {
        cout << *it << " ";
    }
    cout << endl;
}

void test1()
{
    vector<int> v1;
    for (int i = 0; i < 10; i++)
    {
        v1.push_back(i);
    }
    printVector(v1);

    if (v1.empty())
    {
        cout << "v1为空" << endl;
    }
    cout << "v1的容量:" << v1.capacity() << endl; // 16,容量永远大于等于size()
    cout << "v1的尺寸:" << v1.size() << endl;     // 10

    // 重新指定大小
    v1.resize(15);
    printVector(v1); // 默认用0填充新位置

    v1.resize(5);
    printVector(v1); // 如果重新指定的比原来短了,超出部分会被删除掉
}

int main()
{
    test1();
    return 0;
}

vector插入和删除

  • push_back()尾部插入元素
  • pop_back()删除最后一个元素
  • insert向指定位置插入元素
  • erase删除迭代器指向的元素
  • clear删除容器中所有元素
#include 
#include 
using namespace std;

// 打印输出容器中的内容
void printVector(vector<int> &v)
{
    for (vector<int>::iterator it = v.begin(); it != v.end(); it++)
    {
        cout << *it << " ";
    }
    cout << endl;
}

void test1()
{
    vector<int> v1;
    for (int i = 0; i < 10; i++)
    {
        v1.push_back(i);
    }
    printVector(v1);

    // 尾部删除一个元素
    v1.pop_back();
    printVector(v1);

    // 插入
    v1.insert(v1.begin(), 100);
    v1.insert(v1.begin(), 2, 100); // 在头部插入n个数据
    printVector(v1);

    // 删除
    v1.erase(v1.begin());           // 删除头部的一个元素
    v1.erase(v1.begin(), v1.end()); // 区间删除

    v1.clear(); // 清空容器中的元素
}

int main()
{
    test1();
    return 0;
}

vector数据存取

#include 
#include 
using namespace std;

// 打印输出容器中的内容
void printVector(vector<int> &v)
{
    for (vector<int>::iterator it = v.begin(); it != v.end(); it++)
    {
        cout << *it << " ";
    }
    cout << endl;
}

void test1()
{
    vector<int> v1;
    for (int i = 0; i < 10; i++)
    {
        v1.push_back(i);
    }
    printVector(v1);

    // 访问方式1
    for (int i = 0; i < v1.size(); i++)
    {
        cout << v1[i] << " "; // 与数组的访问方式非常相似
    }
    cout << endl;

    // 通过at访问元素
    for (int i = 0; i < v1.size(); i++)
    {
        cout << v1.at(i) << " ";
    }
    cout << endl;

    // 获取第一个元素
    cout << v1.front() << endl;
    // 获取最后一个元素
    cout << v1.back() << endl;
}

int main()
{
    test1();
    return 0;
}

vector互换容器

  • 功能描述:实现两个容器内元素进行互换
#include 
#include 
using namespace std;

// 打印输出容器中的内容
void printVector(vector<int> &v)
{
    for (vector<int>::iterator it = v.begin(); it != v.end(); it++)
    {
        cout << *it << " ";
    }
    cout << endl;
}

void test1()
{
    // 容器1
    vector<int> v1;
    for (int i = 0; i < 10; i++)
    {
        v1.push_back(i);
    }
    printVector(v1);

    // 容器2
    vector<int> v2;
    for (int i = 10; i > 0; i--)
    {
        v2.push_back(i);
    }
    printVector(v2);

    // 交换容器
    v1.swap(v2); // v1与v2进行元素交换
    printVector(v1);
    printVector(v2);
}

int main()
{
    test1();
    return 0;
}

vector预留空间

  • 功能描述:减少vector在动态扩展容量时的扩展次数
  • 函数原型:reserve(int len)容器预留len
#include 
#include 
using namespace std;

// vector容器,当内存空间不足时,自动开辟内存
void test1()
{
    int num = 0; // 统计空间开辟次数
    int *p = NULL;

    // 容器1
    vector<int> v1;

    v1.reserve(10); // 利用reserve预留空间
    for (int i = 0; i < 10; i++)
    {
        v1.push_back(i);
        if (p != &v1[0])
        {
            p = &v1[0]; // 每次重新开辟空间时,从首地址0开始
            num++;
        }
    }
    cout << num << endl; // 使用v1.reserve(10)之后,输出1,不用时,输出5,也就是开辟了5次空间
}

int main()
{
    test1();
    return 0;
}

deque容器基本概念

功能:双端队列,可以对头部进行插入删除操作

  • deque与vector区别:
    • vector对于头部的插入删除效率较低,数据量越大,效率越低
    • vector只能使用push_back()进行尾插
    • deque相对而言,对头部的插入删除速度比vector快
    • vector访问元素的速度比deque快,这和两者内部实现有关
      [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-vdrO6B2G-1685616761117)(images/7.png)]
#include 
#include 
using namespace std;

// 只读迭代器,防止数据被意外的修改
void printDeque(const deque<int> &d)
{
    for (deque<int>::const_iterator it = d.begin(); it != d.end(); it++)
    {
        cout << *it << " ";
    }
    cout << endl;
}

void test1()
{
    deque<int> d1;
    for (int i = 0; i < 10; i++)
    {
        d1.push_back(i);
    }
    printDeque(d1);

    // 采用区间的方式进行赋值
    deque<int> d2(d1.begin(), d1.end());
    printDeque(d2);

    // 赋值方式3
    deque<int> d3(10, 100); // 10个100
    printDeque(d3);

    // 拷贝构造的方式
    deque<int> d4(d3); // 拷贝d3
    printDeque(d4);
}

int main()
{
    test1();
    return 0;
}

deque赋值操作

vector赋值操作一样

#include 
#include 
using namespace std;

// 只读迭代器,防止数据被意外的修改
void printDeque(const deque<int> &d)
{
    for (deque<int>::const_iterator it = d.begin(); it != d.end(); it++)
    {
        cout << *it << " ";
    }
    cout << endl;
}

void test1()
{
    deque<int> d1;
    for (int i = 0; i < 10; i++)
    {
        d1.push_back(i);
    }
    printDeque(d1);

    // 等号赋值
    deque<int> d2;
    d2 = d1;
    printDeque(d2);

    // assign赋值
    deque<int> d3;
    d3.assign(d1.begin(), d1.end());
    printDeque(d3);

    deque<int> d4;
    d4.assign(10, 100);
    printDeque(d4);
}

int main()
{
    test1();
    return 0;
}

deque容器大小操作

deque相比于vector容器,没有容量的概念。

#include 
#include 
using namespace std;

// 只读迭代器,防止数据被意外的修改
void printDeque(const deque<int> &d)
{
    for (deque<int>::const_iterator it = d.begin(); it != d.end(); it++)
    {
        cout << *it << " ";
    }
    cout << endl;
}

void test1()
{
    deque<int> d1;
    for (int i = 0; i < 10; i++)
    {
        d1.push_back(i);
    }
    printDeque(d1);

    if (d1.empty())
    {
        cout << "容器为空" << endl;
    }

    // 元素个数
    cout << "d1的大小:" << d1.size() << endl; // 输出10

    // 重新指定大小
    d1.resize(15);    // 多出的部分用0进行填充
    d1.resize(15, 1); // 多出的部分用1进行填充
    d1.resize(3);     // 超出的部分被删除
}

int main()
{
    test1();
    return 0;
}

deque插入和删除

功能描述:向deque容器中插入和删除数据。

#include 
#include 
using namespace std;

// 只读迭代器,防止数据被意外的修改
void printDeque(const deque<int> &d)
{
    for (deque<int>::const_iterator it = d.begin(); it != d.end(); it++)
    {
        cout << *it << " ";
    }
    cout << endl;
}

// 两端插入和删除操作
void test1()
{
    deque<int> d1;
    d1.push_back(1); // 尾插
    d1.push_back(2);

    d1.push_front(3); // 头插
    d1.push_front(4);
    printDeque(d1); // 输出4 3 1 2

    d1.pop_back();  // 尾删一个元素
    d1.pop_front(); // 头删一个元素
    printDeque(d1); // 输出 3 1
}

// 指定位置插入和删除
void test2()
{
    deque<int> d1;
    d1.push_back(1); // 尾插
    d1.push_back(2);

    d1.push_front(3); // 头插
    d1.push_front(4);
    printDeque(d1); // 输出4 3 1 2

    // insert插入
    d1.insert(d1.begin(), 10);    // 在头部插入一个元素
    d1.insert(d1.begin(), 2, 10); // 在头部插入2个元素

    // 按照区间进行插入
    deque<int> d2;
    for (int i = 0; i < 3; i++)
    {
        d2.push_back(i);
    }
    printDeque(d2);

    d1.insert(d1.begin(), d2.begin(), d2.end()); // 在d1的头部插入d2的区间元素
    printDeque(d1);

    // 删除操作
    deque<int>::iterator it = d1.begin(); // 创建一个迭代器
    it++;
    d1.erase(it); // 删除第二个元素

    // 按区间方式进行删除
    d1.erase(d1.begin(),d1.end());
    d1.clear(); // 效果等同于上面的语句
}

int main()
{
    // test1();
    test2();
    return 0;
}

deque数据存取

功能描述:对deque中的数据进行存取操作。

#include 
#include 
using namespace std;

void test1()
{
    deque<int> d1;
    for (int i = 0; i < 10; i++)
    {
        d1.push_back(i);
    }

    // 通过[]访问
    for (int i = 0; i < d1.size(); i++)
    {
        cout << d1[i] << " ";
    }
    cout << endl;

    // 通过at方式访问元素
    for (int i = 0; i < d1.size(); i++)
    {
        cout << d1.at(i) << " ";
    }
    cout << endl;

    // 访问头尾元素
    cout << "第一个元素:" << d1.front() << endl;
    cout << "最后一个元素:" << d1.back() << endl;
}

int main()
{
    test1();
    return 0;
}

deque排序

功能描述:利用算法sort实现对deque容器进行排序。

  • 需要添加标准算法头文件#include
  • 对于支持随机访问的迭代器的容器,都可以利用sort算法直接进行其排序,例如vector容器
#include 
#include 
#include  // 标准算法头文件
using namespace std;

// 只读迭代器,防止数据被意外的修改
void printDeque(const deque<int> &d)
{
    for (deque<int>::const_iterator it = d.begin(); it != d.end(); it++)
    {
        cout << *it << " ";
    }
    cout << endl;
}

void test1()
{
    deque<int> d1;
    d1.push_back(10);
    d1.push_back(8);
    d1.push_front(0);
    d1.push_front(1);
    printDeque(d1); // 输出1 0 10 8

    // 排序:默认规则为从小到大排序
    sort(d1.begin(), d1.end());
    printDeque(d1); // 升序
}

int main()
{
    test1();
    return 0;
}

stack基本概念

stack是一种先进后出(后进先出)(First In Last Out,FILO)的数据结构,只有一个出口。

  • 栈中只有顶部的元素才可以被外界使用,因此栈不允许有遍历行为。即不能使用迭代器遍历数据
  • 栈是自顶向下的,堆是向上的。
  • 入栈push,出栈pop,生活中的栈实例:弹夹
    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-T6ibZ14K-1685616770382)(images/8.png)]

stack常用接口

  • 栈顶元素先出去,后面的元素才能出去
#include 
#include 
using namespace std;

void test1()
{
    stack<int> s;

    // 入栈
    for (int i = 0; i < 4; i++)
    {
        s.push(i);
    }
    cout << "栈的大小:" << s.size() << endl; // 输出:4

    // 只要栈不为空,查看栈顶,并且执行出栈操作
    while (!s.empty())
    {
        cout << "栈顶元素为:" << s.top() << endl;
        s.pop(); // 出栈,才能访问下一个元素
    }

    cout << "栈的大小:" << s.size() << endl; // 数据元素全部出栈后,栈的大小为0
}

int main()
{
    test1();
    return 0;
}

queue基本概念

  • queue是一种先进先出(First In First Out,FIFO)的数据结构,它有两个出口。
    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-DQ5Jf66P-1685616780640)(images/9.png)]
  • 队列中只有队头和队尾才可以被外界使用,因此队列不允许有遍历行为。即不能使用迭代器遍历数据

常用接口

#include 
#include 
using namespace std;

void test1()
{
    queue<int> q; // 创建一个队列容器
    q.push(10);   // 先进先出
    q.push(1);
    q.push(2);
    cout << "队列大小:" << q.size() << endl; // 输出3
    while (!q.empty())
    {
        // 查看队头
        cout << "对头:" << q.front() << endl;
        cout << "队尾:" << q.back() << endl;
        q.pop(); // 出队,要不是一个死循环
    }

    cout << "队列大小:" << q.size() << endl; // 输出0
}

int main()
{
    test1();
    return 0;
}

list基本概念

  • 功能:将数据进行链式存储

  • 链表的优点:可以对任意位置进行快速插入或删除元素

  • 缺点:容器的遍历速度没有数组快,占用的空间比数组大(有指针域的存在)

  • 链表(list)是一种物理存储单元上非连续的存储结构,数据元素的逻辑顺序是通过链表中的指针链接实现的

  • 链表的组成:链表由一系列结点组成

  • 结点的组成:一个是存储数据元素的数据域,另一个是存储下一个结点地址的指针域
    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-TBlPmX2P-1685616790228)(images/10.png)]

  • STL中的链表是一个双向循环链表
    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-CetDVMh5-1685616790229)(images/11.png)]

  • 由于链表的存储方式并不是连续的内存空间,因此链表list中的迭代器只支持前移和后移,属于双向迭代器

  • list的优点:

    • 采用动态存储分配,不会造成内存浪费和溢出
    • 链表执行插入和删除操作十分方便,修改指针即可,不需要一段大量元素
  • list的缺点:

    • 链表灵活,但是空间(指针域)和时间(遍历)额外耗费较大
      list有一个重要的性质,插入和删除操作都不会造成原有list迭代器的失效,这在vector是不成立的。
      总结:STL中list和vector是两个最常用的容器,各有优缺点

list构造函数

#include 
#include 
using namespace std;

void printList(const list<int> &L)
{
    for (list<int>::const_iterator it = L.begin(); it != L.end(); it++)
    {
        cout << *it << " ";
    }
    cout << endl;
}

void test1()
{
    list<int> L1;
    L1.push_back(1);
    L1.push_back(2);
    L1.push_back(3);

    printList(L1); // 输出1 2 3

    list<int> L2(L1.begin(), L1.end()); // 区间方式构造
    printList(L2);

    list<int> L3(L2); // 拷贝构造
    printList(L3);

    list<int> L4(10, 100); // 赋值10个100
    printList(L4);
}

int main()
{
    test1();
    return 0;
}

list赋值和交换

#include 
#include 
using namespace std;

void printList(const list &L)
{
    for (list::const_iterator it = L.begin(); it != L.end(); it++)
    {
        cout << *it << " ";
    }
    cout << endl;
}

void test1()
{
    list L1;
    L1.push_back(1);
    L1.push_back(2);
    L1.push_back(3);

    printList(L1); // 输出1 2 3

    list L2 = L1; // 直接赋值
    printList(L2);

    list L3;
    L3.assign(L2.begin(), L2.end()); // assign赋值
    printList(L3);

    list L4;
    L4.assign(10, 100);
    printList(L4);
}

// 交换
void test2()
{
    list L1;
    L1.push_back(1);
    L1.push_back(2);
    L1.push_back(3);

    list L2;
    L2.assign(10, 100);

    cout << "交换前:" << endl;
    printList(L1);
    printList(L2);

    cout << "交换后:" << endl;
    L1.swap(L2);
    printList(L1);
    printList(L2);
}

int main()
{
    // test1();
    test2();
    return 0;
}

list容器大小操作

#include 
#include 
using namespace std;

void printList(const list<int> &L)
{
    for (list<int>::const_iterator it = L.begin(); it != L.end(); it++)
    {
        cout << *it << " ";
    }
    cout << endl;
}

void test1()
{
    list<int> L1;
    L1.push_back(1);
    L1.push_back(2);
    L1.push_back(3);

    printList(L1); // 输出1 2 3

    if (!L1.empty()) // 判断容器是否为空
    {
        cout << "L1元素个数:" << L1.size() << endl;
    }

    // 重新指定容器大小
    L1.resize(10);
    printList(L1);
}

int main()
{
    test1();
    return 0;
}

list插入和删除

#include 
#include 
using namespace std;

void printList(const list<int> &L)
{
    for (list<int>::const_iterator it = L.begin(); it != L.end(); it++)
    {
        cout << *it << " ";
    }
    cout << endl;
}

void test1()
{
    list<int> L1;
    L1.push_back(1); // 尾插
    L1.push_back(2);

    L1.push_front(3); // 头插
    L1.push_front(4);
    printList(L1); // 输出4 3 1 2

    L1.pop_back();  // 尾部删除一个元素
    L1.pop_front(); // 头部删除一个元素

    // insert
    L1.insert(L1.begin(), 1000);

    list<int>::iterator it = L1.begin();
    L1.insert(++it, 10); // 创建一个迭代器,在其他位置插入元素

    // 删除
    L1.erase(L1.begin()); // 移除头部的一个元素

    // 移除
    L1.remove(100); // 删除容器中所有与元素值匹配的元素

    L1.clear(); // 清空
}

int main()
{
    test1();
    return 0;
}

list数据存取

  • 由于list在内存中不是一块连续的空间,因此不支持[]at()的方式访问元素
    注意事项:list迭代器不支持随机访问。其他迭代器同样的验证方法:
    list<int>::iterator it = L1.begin();
    it++; // 支持,可以通过多加几次,访问其他元素
    it--; // 支持

    it = it + 1; // 不支持
    it = it + 2; // 不支持
  • front()返回第一个元素
  • back()返回最后一个元素

list反转和排序

功能描述:将容器中的元素反转,以及将容器中的数据进行排序。

  • reverse()反转链表
  • sort()链表排序,所有不支持随机访问迭代器的容器,不可以使用标准算法。不支持随机访问迭代器的容器,内部会提供对应的一些算法
#include 
#include 
using namespace std;

void printList(const list<int> &L)
{
    for (list<int>::const_iterator it = L.begin(); it != L.end(); it++)
    {
        cout << *it << " ";
    }
    cout << endl;
}

bool myCompare(int v1, int v2)
{
    // 降序:让第一个数>第二个数
    return v1 > v2;
}

void test1()
{
    list<int> L1;
    L1.push_back(1); // 尾插
    L1.push_back(2);

    L1.push_front(3); // 头插
    L1.push_front(4);

    cout << "反转前:";
    printList(L1); // 输出4 3 1 2

    cout << "反转后:";
    L1.reverse(); // 反转
    printList(L1);

    // 排序
    cout << "排序前:";
    printList(L1);
    cout << "排序后(升序):";
    L1.sort(); // 默认排序规则:从小到大升序
    printList(L1);

    // 通过仿函数实现降序排序
    cout << "排序后(降序):";
    L1.sort(myCompare); // 传入自定义的仿函数名
    printList(L1);
}

int main()
{
    test1();
    return 0;
}

set基本概念

  • 简介:所有元素在插入时自动被排序
  • 本质:set/multiset属于关联式容器,底层结构是用二叉树实现的。
    set和multiset区别:
  • set不允许容器中有重复的元素
  • multiset允许容器中有重复的元素
#include 
#include 
using namespace std;

void printSet(set<int> &s)
{
    for (set<int>::iterator it = s.begin(); it != s.end(); it++)
    {
        cout << *it << " ";
    }
    cout << endl;
}

// set容器构造和赋值
void test1()
{
    set<int> s1;
    s1.insert(1); // 插入数据只有insert方式
    s1.insert(0); // set容器不允许插入重复值
    s1.insert(3);

    printSet(s1); // 遍历容器,自动输出排序后(升序)的值

    set<int> s2(s1); // 拷贝构造

    // 赋值方式
    set<int> s3;
    s3 = s2;
}

int main()
{
    test1();
    return 0;
}

set大小和交换

#include 
#include 
using namespace std;

void printSet(set<int> &s)
{
    for (set<int>::iterator it = s.begin(); it != s.end(); it++)
    {
        cout << *it << " ";
    }
    cout << endl;
}

void test1()
{
    set<int> s1;
    s1.insert(1); // 插入数据只有insert方式
    s1.insert(0); // set容器不允许插入重复值
    s1.insert(3);

    if (!s1.empty())
    {
        cout << "元素的个数:" << s1.size() << endl;
    }

    set<int> s2;
    s2.insert(2);
    s2.insert(1);

    cout << "交换前:" << endl;
    printSet(s1);
    printSet(s2);
    cout << "交换后:" << endl;
    s1.swap(s2);
    printSet(s1);
    printSet(s2);
}

int main()
{
    test1();
    return 0;
}

set插入和删除

#include 
#include 
using namespace std;

void printSet(set<int> &s)
{
    for (set<int>::iterator it = s.begin(); it != s.end(); it++)
    {
        cout << *it << " ";
    }
    cout << endl;
}

void test1()
{
    set<int> s1;
    s1.insert(1); // 插入数据只有insert方式
    s1.insert(0); // set容器不允许插入重复值
    s1.insert(3);

    s1.erase(s1.begin()); // 删除头部元素(自动排序后的头部元素)
    printSet(s1);

    s1.erase(3);                    // 删除指定元素
    s1.erase(s1.begin(), s1.end()); // 清空
    s1.clear();                     // 同上
}

int main()
{
    test1();
    return 0;
}

set查找和统计

  • find(key)查找key是否存在,若存在,返回该元素的迭代器。若不存在,返回set.end()
  • count(key)统计key的元素个数,对于set容器而言,返回值为0或1
#include 
#include 
using namespace std;

void test1()
{
    set<int> s1;
    s1.insert(1);
    s1.insert(0);
    s1.insert(3);

    set<int>::iterator pos = s1.find(3);
    if (pos != s1.end())
    {
        cout << "找到元素:" << *pos << endl;
    }
    else
    {
        cout << "未找到" << endl;
    }

    int num = s1.count(0); // 统计元素0的个数
    cout << num << endl;
}

int main()
{
    test1();
    return 0;
}

set和multiset区别

  1. set不可以插入重复数据,而multiset可以
  2. set插入数据的同时会返回插入结果,表示插入是否成功
  3. multiset不会检测数据,因此可以插入重复数据
#include 
#include 
using namespace std;

void printMultiset(multiset<int> &ms)
{
    for (multiset<int>::iterator it = ms.begin(); it != ms.end(); it++)
    {
        cout << *it << " ";
    }
    cout << endl;
}

void test1()
{
    set<int> s1;
    s1.insert(1);
    s1.insert(0);
    s1.insert(3);

    pair<set<int>::iterator, bool> ret = s1.insert(10);
    if (ret.second)
    {
        cout << "第一次插入成功" << endl;
    }
    else
    {
        cout << "第一次插入失败" << endl;
    }

    ret = s1.insert(10); // 插入重复值,会失败
    if (ret.second)
    {
        cout << "第二次插入成功" << endl;
    }
    else
    {
        cout << "第二次插入失败" << endl;
    }

    // multiset允许插入重复值
    multiset<int> ms;
    ms.insert(10);
    ms.insert(10);
    printMultiset(ms); // 输出:10 10
}

int main()
{
    test1();
    return 0;
}

pair对组创建

功能描述:成对出现的数据,利用对组可以返回两个数据

#include 
#include 
using namespace std;

void test1()
{
    // 第一种创建方式
    pair<string, int> p1("Tom", 20); // 数据类型,数据值
    cout << "姓名:" << p1.first << "年龄:" << p1.second << endl;

    // 第二种创建方式
    pair<string, int> p2 = make_pair("小明", 18);
    cout << "姓名:" << p2.first << "年龄:" << p2.second << endl;
}

int main()
{
    test1();
    return 0;
}

set容器排序

学习目标:set容器默认排序规则为从小到大,掌握如何改变排序规则。
主要技术点:利用仿函数,可以改变排序规则。
示例1:set存放内置数据类型

#include 
#include 
using namespace std;

void printSet(set<int> &s)
{
    for (set<int>::iterator it = s.begin(); it != s.end(); it++)
    {
        cout << *it << " ";
    }
    cout << endl;
}

class MyCompare
{
public:
    bool operator()(int v1, int v2)
    {
        return v1 > v2;
    }
};

void printSet2(set<int, MyCompare> &s)
{
    for (set<int, MyCompare>::iterator it = s.begin(); it != s.end(); it++)
    {
        cout << *it << " ";
    }
    cout << endl;
}

void test1()
{
    set<int> s1;
    s1.insert(10);
    s1.insert(6);
    s1.insert(7);

    printSet(s1); // 输出 6 7 10

    // 指定排序规则为从大到小
    set<int, MyCompare> s2; // 仿函数本质上是一个数据类型
    s2.insert(10);
    s2.insert(6);
    s2.insert(7);
    printSet2(s2); // 输出 10 7 6
}

int main()
{
    test1();
    return 0;
}

set容器自定义数据类型指定排序规则

  • 自定义的数据类型,都需要采用仿函数指定排序规则
#include 
#include 
#include 
using namespace std;

// 创建自定义数据类型
class Person
{
public:
    string m_name;
    int m_age;
    Person(string name, int age)
    {
        this->m_name = name;
        this->m_age = age;
    }
};

// 指定排序规则
class MyCompare
{
public:
    bool operator()(const Person &p1, const Person &p2)
    {
        // 按照年龄降序
        return p1.m_age > p2.m_age;
    }
};

void printSet(set<Person, MyCompare> &s)
{
    for (set<Person, MyCompare>::iterator it = s.begin(); it != s.end(); it++)
    {
        cout << "姓名:" << it->m_name << ",年龄:" << it->m_age << endl;
    }
}

void test1()
{
    set<Person, MyCompare> s;
    Person p1("刘备", 1);
    Person p2("关羽", 3);
    Person p3("张飞", 2);

    s.insert(p1);
    s.insert(p2);
    s.insert(p3);

    printSet(s);
}

int main()
{
    test1();
    return 0;
}

你可能感兴趣的:(C++知识库,c++,笔记)