# C++ 数据结构 集合 等 数据容器 汇总! *C++ 技术栏* 在这里我们描述了在 C++ 中常见的数据容器,您可以在这里获取到他们的特性以及使用方式! ## 目录 [TOC]  # Vector ## 迭代器访问 Vector的迭代器其实就是数组中的元素指针,不过更方便我们使用,访问元素的步骤为:先获取到起始与结束位置的迭代器,然后不断移动起始迭代器的位置,直到其实迭代器的位置与结束迭代器的位置相同的时候,则代表迭代结束。 值得注意的是 在这里的终止指针其实就是一个哨兵!!!因此it2 的前一个才是元素! ### begin end 指针 - 手动操作指针 下面是一个基础的语法演示 ``` // 获取到起始位置 auto vb = vector1.begin(); // 获取到终止位置 auto ve = vector1.end(); // 判断是否为同一个位置 vb == ve ``` 根据上面的语法,我们可以像下面这样使用它! ``` #include <vector> #include <iostream> int main() { std::vector<int> vector{2, 4, 6, 8, 10, 1024, 1000}; // 获取到起始和终止指针 auto it1 = vector.begin(), it2 = vector.end(); // 循环移动指针 直到 it1 到达 it2 的位置 while (it1 != it2) { // 在这里使用 ++ 让 it1 自增!就相当于是在向后移动了 std::cout << *it1++ << std::endl; } } ``` ### begin end 指针 - foreach + fun对象 自动操作指针 下面的操作将会自动的帮助我们迭代指针,更加的简洁! ``` // 导入STL函数库 #include "algorithm" // 直接迭代 std::for_each(起始指针, 终止指针, 操作函数(函数的形参类型是被迭代的元素类型)); ``` 在下面就是一个举例! ``` #include <vector> #include <iostream> #include <algorithm> void fun(int number){ std::cout << number << std::endl; } int main() { std::vector<int> vector{2, 4, 6, 8, 10, 1024, 1000}; // 获取到起始和终止指针 auto it1 = vector.begin(), it2 = vector.end(); // 循环移动指针 并将每个元素传递给 fun 函数 std::for_each(it1, it2, fun); } ``` ## 泛型支持 下面的演示中,我们可以通过修改尖括号来实现自定义数据容器中的类型! ``` std::vector<泛型类型> myVector1; ``` ### 准备一个自定义类型 ``` #include <vector> #include <iostream> class User { std::string name; int age; public: User(std::string name, int age) : name(std::move(name)), age(age) {} std::string getName() { return this->name; } int getAge() const { return this->age; } }; // 使用这个函数来封装打印逻辑 std::ostream &operator<<(std::ostream &ostream, User &user) { return ostream << user.getName() << ' ' << user.getAge(); } ``` ### 准备一个自定义类型的容器并使用 ``` #include <vector> #include <iostream> class User { std::string name; int age; public: User(std::string name, int age) : name(std::move(name)), age(age) {} std::string getName() { return this->name; } int getAge() const { return this->age; } }; // 使用这个函数来封装打印逻辑 std::ostream &operator<<(std::ostream &ostream, User &user) { return ostream << user.getName() << ' ' << user.getAge(); } // TODO 从这里开始就是自定义类型的代码了 #include <utility> #include <algorithm> void fun(User user) { std::cout << user << std::endl; } int main() { std::vector<User> vector{ User{"zhao", 21}, User{"yang", 20} }; // 获取到起始和终止指针 auto it1 = vector.begin(), it2 = vector.end(); // 循环移动指针 并将每个元素传递给 fun 函数 std::for_each(it1, it2, fun); } ``` ## 构造与创建 ``` // 创建一些向量 具有固定数值的 vector<int> vector1{1, 2, 3, 4}; // 指针拷贝构造 通常用来进行复制元素操作 vector<int> vector2{vector1.begin(), vector1.end()}; // n 个数值填充构造 快捷的指定元素和长度 vector<int> vector3(4, 7); // 拷贝构造函数 (深拷贝) vector<int> vector4(vector1); ``` ## 修改与赋值 ### 修改 - 修改一个容器中指定部分的元素为某个新元素 ``` #include <vector> #include <iostream> // 这个函数是用来将 vector 快速迭代的一个函数 std::ostream &operator<<(std::ostream &ostream, std::vector<int> v) { auto it1 = v.begin(), it2 = v.end(); while (it1 != it2) ostream << *it1++ << ' '; return ostream; } int main() { std::vector<int> v{1, 2, 3, 4, 5}; // 通过索引修改元素 v[2] = 1024; // 直接打印 vector 的所有元素 std::cout << v << std::endl; } ``` ### 赋值 - 修改一个容器中所有的元素为某个容器的元素 在下面是一个基础的使用示例 ``` std::vector<int> v1{1, 2, 3, 4, }; std::vector<int> v2{10, 20, 300, 4, 5}; // 将 v1 中的元素 替换为 v2 中的元素 // 不论 v1 中的元素长度如何,在这里都会进行替换 // 最终长度和 v2 一致 数据和 v2 一致 这就是覆盖! v1.assign(v2.begin(), v2.end()); ``` 我们可以像下面一样使用它! ``` #include <vector> #include <iostream> std::ostream &operator<<(std::ostream &ostream, std::vector<int> v) { auto it1 = v.begin(), it2 = v.end(); while (it1 != it2) ostream << *it1++ << ' '; return ostream; } int main() { std::vector<int> v1{1, 2, 3, 4, }; std::vector<int> v2{10, 20, 300, 4, 5}; // 将 v1 中的元素 替换为 v2 中的元素 // 不论 v1 中的元素长度如何,在这里都会进行替换 // 最终长度和 v2 一致 数据和 v2 一致 这就是覆盖! v1.assign(v2.begin(), v2.end()); // 打印结果 std::cout << v1 << std::endl; } ``` ## 容量和大小 ### 判断是否为空 - empty ``` #include <vector> #include <iostream> int main() { std::vector<int> v1{}; // 判断容器是否为空 这打印出来的是 1 代表 true std::cout << v1.empty() << std::endl; // 添加一个元素之后再次判断容器是否为空 这里打印出来的是 0 代表 false v1.push_back(1024); std::cout << v1.empty() << std::endl; } ``` ### 获取到容器的容量 - capacity capacity 能够有效的将一个容器的实际长度获取到,容器的容量一般都是会有些冗余的,在这可以看到实际占用多少个元素的空间! > 值得注意的是,每次追加元素都可能导致容器容量发生变化,因为他们需要进行扩充! ``` #include <vector> #include <iostream> int main() { std::vector<int> v1{}; // 这个时候 元素里面是空的,容量也是 0 std::cout << v1.capacity() << std::endl; // 添加一些元素之后再次判断容器容器的长度 这个时候是 4 容器被扩充了 v1.push_back(1024); v1.push_back(1024); v1.push_back(1024); std::cout << v1.capacity() << std::endl; } ``` ### 获取到容器中元素的个数 - size 与 capacity 不同的是,size 只返回元素的长度,对于实际长度并不关心,一般情况下,size 可能会更加常用。 ``` #include <vector> #include <iostream> int main() { std::vector<int> v1{}; // 判断容器元素数量 这里是 0 没有元素! std::cout << v1.size() << std::endl; // 添加一些元素之后再次判断容器元素数量 这里是 3 v1.push_back(1024); v1.push_back(1024); v1.push_back(1024); std::cout << v1.size() << std::endl; } ``` ### 重新设定容器中元素的个数 - resize  #### 不设置填充数值 ``` #include <vector> #include <iostream> std::ostream &operator<<(std::ostream &ostream1, std::vector<int> &v) { for (const auto &item: v) { ostream1 << item << '\t'; } return ostream1; } int main() { std::vector<int> v1{1, 2, 3, 4, 5, 6}; // 将容器的长度重新设置为 10 // 这个时候会比原先多出来 3 个元素,我们这里没有指定填充的数值 C++ 会默认填充 0 v1.resize(10); // 打印出来看下 std::cout << v1 << std::endl; // 如果缩小,则超出部分将会删除 优先删除右边的数据! v1.resize(4); // 打印出来看下 std::cout << v1 << std::endl; } ```  #### 设置填充数值 ``` #include <vector> #include <iostream> std::ostream &operator<<(std::ostream &ostream1, std::vector<int> &v) { for (const auto &item: v) { ostream1 << item << '\t'; } return ostream1; } int main() { std::vector<int> v1{1, 2, 3, 4, 5, 6}; // 将容器的长度重新设置为 10 // 这个时候会比原先多出来 3 个元素,我们这里没有指定填充的数值 C++ 会默认填充 0 v1.resize(10, 1024); // 打印出来看下 std::cout << v1 << std::endl; // 如果缩小,则超出部分将会删除 优先删除右边的数据! v1.resize(4, 1024); // 打印出来看下 std::cout << v1 << std::endl; } ```  ## 插入和删除 ### 在容器的尾部插入 - push_back ``` #include <iostream> #include <vector> using namespace std; ostream &operator<<(ostream &ostream1, vector<int> &vector) { for (const auto &item: vector) { cout << item << '\t'; } return ostream1; } int main() { // 创建一些向量 vector<int> vector1{1, 2, 3, 4}; cout << vector1 << endl; // 在尾部插入一个元素 vector1.push_back(1024); cout << vector1 << endl; } ``` ### 删除最后一个元素 - pop_back ``` #include <iostream> #include <vector> using namespace std; ostream &operator<<(ostream &ostream1, vector<int> &vector) { for (const auto &item: vector) { cout << item << '\t'; } return ostream1; } int main() { // 创建一些向量 vector<int> vector1{1, 2, 3, 4}; cout << vector1 << endl; // 将最后一个元素删掉 vector1.pop_back(); // 再一次打印 cout << vector1 << endl; } ``` ### 在迭代器指定位置插入 - insert > 值得注意的是,插入成功之后,所有的元素会向后移动! ``` #include <iostream> #include <vector> using namespace std; ostream &operator<<(ostream &ostream1, vector<int> &vector) { for (const auto &item: vector) { cout << item << '\t'; } return ostream1; } int main() { // 创建一些向量 vector<int> vector1{1, 2, 3, 4}; cout << vector1 << endl; // 获取到第 2 索引元素对应的迭代器指针 auto it = vector1.begin() + 2; // 在这个位置插入一个元素 1024 vector1.insert(it, 1024); cout << vector1 << endl; // 重新获取到第 2 索引迭代器指针 it = vector1.begin() + 2; // 在第 2 索引位置插入 3 个 2048 vector1.insert(it, 3, 2048); cout << vector1 << endl; } ``` ### 删除迭代器指定位置的元素 - erase #### 删除指定位置的元素 ``` #include <iostream> #include <vector> using namespace std; ostream &operator<<(ostream &ostream1, vector<int> &vector) { for (const auto &item: vector) { cout << item << '\t'; } return ostream1; } int main() { // 创建一些向量 vector<int> vector1{1, 2, 3, 4, 5, 6, 7, 8, 9}; cout << vector1 << endl; // 获取到第 2 索引元素对应的迭代器指针 auto it = vector1.begin() + 2; // 将第 2 索引元素删除 vector1.erase(it); // 最后查看结果 cout << vector1 << endl; } ```  #### 删除指定范围的元素 ``` #include <iostream> #include <vector> using namespace std; ostream &operator<<(ostream &ostream1, vector<int> &vector) { for (const auto &item: vector) { cout << item << '\t'; } return ostream1; } int main() { // 创建一些向量 vector<int> vector1{1, 2, 3, 4, 5, 6, 7, 8, 9}; cout << vector1 << endl; // 获取到第 2 索引元素对应的迭代器指针 auto it2 = vector1.begin() + 2; // 获取到第 5 索引元素对应的迭代器指针 auto it5 = vector1.begin() + 5; // 将第 2 ~5 索引元素删除 值得注意的是,因为数据结构中哨兵模式的存在 最后一个指针往往不一定有数值 // 因此为了避免一些不便的情况,这里的区间是左闭右开区间,因此我们需要对 it5 + 1 才能删掉 5 索引 vector1.erase(it2, it5 + 1); // 最后查看结果 cout << vector1 << endl; } ```  ### 删除容器中所有的元素 - clear ``` #include <iostream> #include <vector> using namespace std; ostream &operator<<(ostream &ostream1, vector<int> &vector) { for (const auto &item: vector) { cout << item << '\t'; } return ostream1; } int main() { // 创建一些向量 vector<int> vector1{1, 2, 3, 4, 5, 6, 7, 8, 9}; cout << vector1 << endl; // 使用 clear 清理所有元素 vector1.clear(); cout << vector1 << endl; } ``` ## 数据存取 ``` // 创建一些向量 vector<int> vector1{1, 2, 3, 4}; // 使用 at 函数获取到 2 索引处的数值 cout << vector1.at(2) << endl; // 使用索引运算符获取到 2 索引处的数值 cout << vector1[2] << endl; // 使用 front 函数获取到数组中的第一个元素 cout << vector1.front() << endl; // 使用 back 函数获取到数组中的最后一个元素 cout << vector1.back() << endl; ``` ## 容器互换 ``` #include <iostream> #include <vector> using namespace std; ostream &operator<<(ostream &ostream1, vector<int> &vector) { for (const auto &item: vector) { cout << item << '\t'; } return ostream1; } int main() { // 创建一些向量 vector<int> vector1{1, 2, 3, 4, 5, 6, 7, 8, 9}, vector2{20, 30, 40, 560}; cout << "vector1:" << vector1 << endl; cout << "vector2:" << vector2 << endl; cout << "交换之后" << endl; // 将两个容器的元素进行交换 vector1.swap(vector2); cout << "vector1:" << vector1 << endl; cout << "vector2:" << vector2 << endl; } ``` ## 调整容器长度! ### 调整容器中 元素长度 的值 - resize ``` #include <iostream> #include <vector> using namespace std; ostream &operator<<(ostream &ostream1, vector<int> &vector) { for (const auto &item: vector) { cout << item << '\t'; } return ostream1; } int main() { // 创建一些向量 vector<int> vector1{1, 2, 3, 4, 5, 6, 7, 8, 9}; // 打印向量的空间 cout << vector1 << endl; // 调整预留空间 在这里我们设置为18 个元素 相当于是追加出 9 个int数据的空间 vector1.resize(18); // 打印其中的元素 可以看见其中现在拓展为 18 个元素了 cout << vector1 << endl; } ``` ### 调整容器中 预留程度 的值 - reserve ``` #include <iostream> #include <vector> using namespace std; ostream &operator<<(ostream &ostream1, vector<int> &vector) { for (const auto &item: vector) { cout << item << '\t'; } return ostream1; } int main() { // 创建一些向量 vector<int> vector1{1, 2, 3, 4, 5, 6, 7, 8, 9}; // 打印向量的空间 cout << vector1.capacity() << endl; // 调整预留空间 在这里我们设置为预留 18 个空间 相当于是追加出 9 个int数据的空间 vector1.reserve(18); // 打印其中的元素 可以看见其中现在拓展为 18 的空间了 cout << vector1.capacity() << endl; // reserve 只能调整预留空间 不会影响到元素长度 cout << vector1 << endl; } ``` # String ## 字符串的构造 ``` // 导入String #include "string" // 导入 io 库 #include "iostream" using namespace std; int main() { // 创建一个空字符串 std::string string1; // 根据char* 创建字符串 std::string string2("zhao"); // 根据一个String 创建一个新字符串 std::string string3(string2 + "123"); // 使用n个字符构建字符串 std::string string4(4, 'a'); // 打印四个字符串 std::cout << string1 << std::endl; std::cout << string2 << std::endl; std::cout << string3 << std::endl; std::cout << string4 << std::endl; } ``` ## 字符串的赋值 ### char 指针 赋值给字符串 - 等号运算符 char 指针 写法就是 `char* c`,很显然这是一个 C语言中使用到的字符串操作,我们可以像下面一样将这个转换为 C++ 中的一个 `string` 对象。 ``` std::string string1 = cs; ``` ``` // 导入String #include "string" // 导入 io 库 #include "iostream" using namespace std; int main() { // 创建一个具有 10 个空间的 char* 指针 char* cs = new char[10]{ 'z', 'h', 'a', 'o', 'z', 'h', 'a', 'o', // 使用 \0 结尾 避免出现读取一些不属于 cs 范围的东西! 'x', '\0' }; // 将 cs 中的数据传递给 string // 最后的结果是 zhaozhaox std::string string1 = cs; cout << string1 << endl; // 最后不要忘记释放 delete[] cs; } ``` ### string对象 赋值给字符串 - 等号运算符 ``` string string1 = "zhao"; string string2 = string1; ``` 下面是一个具体的示例! ``` // 导入String #include "string" // 导入 io 库 #include "iostream" using namespace std; int main() { string string1 = "zhao"; string string2 = string1; cout << string1 << endl; cout << string2 << endl; if (std::equal(string1.begin(), string1.end(), string2.begin())){ cout << "两个字符串相同!" << endl; } } ``` ### char 对象 赋值给字符串 - 等号运算符 ``` // 导入String #include "string" #include "iostream" using namespace std; int main() { char c = 'z'; string string2 = &c; cout << string2 << endl; } ``` ------ ***操作记录*** 作者:[zhao](https://www.lingyuzhao.top//index.html?search=4 "zhao") 操作时间:2024-07-04 17:09:36 星期四 事件描述备注:保存/发布 中国 天津 [](如果不需要此记录可以手动删除,每次保存都会自动的追加记录)