C++中的常见容器使用方法

vector

C++ std::vector 总结

std::vector 是 C++ 标准库中的一个序列容器,它封装了动态大小的数组。以下是关于 std::vector 的一些关键点和使用注意事项:

  1. 存储方式std::vector 中的元素是连续存储的,这意味着不仅可以通过迭代器访问元素,还可以使用偏移量来访问元素的常规指针。

  2. 自动管理存储std::vector 会自动处理其存储,根据需要进行扩展。通常,向量占用的空间比静态数组多,因为更多的内存被分配用于处理未来的增长。这样,每次插入元素时,向量不需要重新分配,只有在额外的内存用尽时才需要。

  3. 性能注意事项:重新分配通常在性能方面是代价高昂的操作。如果事先知道元素的数量,可以使用 reserve() 函数来避免重新分配。

  4. 操作复杂度

    • 随机访问 - 常数时间 O(1)。
    • 在末尾插入或删除元素 - 平摊常数时间 O(1)。
    • 插入或删除元素 - 线性时间 O(n)。
  5. 迭代器失效:某些操作可能导致迭代器失效。例如,如果向量更改了容量,push_backemplace_back 可能会使所有迭代器失效。erase 会使被删除的元素和它们之后的所有元素的迭代器失效。

  6. 特化:标准库为 bool 类型提供了 std::vector 的特化,可能针对空间效率进行了优化。

  7. 示例

    #include <iostream>
    #include <vector>
    
    int main()
    {
        // 创建一个包含整数的向量
        std::vector<int> v = {8, 4, 5, 9};
        
        // 向向量添加两个整数
        v.push_back(6);
        v.push_back(9);
        
        // 覆盖位置 2 的元素
        v[2] = -1;
        
        // 打印向量
        for (int n : v)
            std::cout << n << ' ';
        std::cout << '\n';
    }
    

    输出:8 4 -1 9 6 9

希望这个总结能帮助您更好地理解和使用 std::vector。您还有其他关于 std::vector 或 C++ 的问题吗?

queue

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

int main() {
    queue<int> q;

    q.push(10); // Enqueue 10
    q.push(20); // Enqueue 20
    q.push(30); // Enqueue 30

    cout << q.front() << endl; // Peek 10
    q.pop(); // Dequeue 10
    cout << q.front() << endl; // Peek 20
    return 0;
}

deque

双向队列,相比queue:

  • 可以被遍历
// 基本用法
std::deque<int> nums {1, 2, 3};
nums.push_back(4); // add at last
nums.push_front(0); // add at first
nums.pop_back(); // remove at last
nums.pop_front(); // remove at first
for (int num : nums) {
    std::cout << num << "->";
}
// 1->2->3->


// 可以遍历
deque<int> myDeque = {1, 2, 3, 4, 5};
cout << "Traversing deque using iterators: ";
for (auto it = myDeque.begin(); it != myDeque.end(); ++it) {
    cout << *it << " ";
}
cout << endl;
// Traversing deque using iterators: 1 2 3 4 5 

unordered_map

std::unordered_map<std::string, size_t> people {{"Jan",44}, {"Jim", 33}, {"Joe", 99}};  // Name,age

// 模板类的默认构造函数,创建空的unordered_map
unordered_map<int, string> umap;

// 使用初始化列表初始化
unordered_map<int, string> umap = unordered_map<int, string>({{1,"a"},{2,"b"}});  // 显式调用C++的构造函数
unordered_map<int, string> umap2({{3,"c"},{4,"d"}});	// 隐式调用构造函数,更简洁
unordered_map<string, string> umap{
    {"淘宝","https://www.taobao.com/"},
    {"京东","https://www.jd.com/"},
    {"天猫商城","https://jx.tmall.com/"} };

// 元素访问
first["GOOG"] = "Google";		// new element inserted
first["AAPL"] = "Apple";		// new element inserted
first["MSFT"] = "Microsoft";	// new element inserted
first["BOB"] = "Bob";
string brand1 = first["GOOG"];	// read
first["BOB"] = "";				// writen
for (auto it = first.begin(); it != first.end(); it++){
    cout << " " << it->first << ":" << it->second;
}
cout<<endl;

/** 插入 **/
unordered_map<string,double> myrecipe, mypantry = {{"milk",2.0},{"flour",1.5}};;					
pair<string,double> myshopping ("baking powder",0.3);
myrecipe.insert(myshopping);	// copy insertion
myrecipe.insert(mypantry.begin(), mypantry.end());	// range inseration
myrecipe.insert({{"sugar",0.8},{"salt",0.1},{"sugar",0.9}});		// initializer list inseration

cout << "myrecipe contains:" << endl;
for(auto& x:myrecipe){
    cout << x.first << ":" << x.second << " "; 
}

/**  遍历 **/
for (auto it = first.begin(); it != first.end(); it++){
    cout << " " << it->first << ":" << it->second;
}
cout<<endl;

std::set

set<int> q;     //以int型为例 默认按键值升序
set<int,greater<int>> p;  //降序排列 
int x;
q.insert(x);	//将x插入q中
q.erase(x);		//删除q中的x元素,返回0或1,0表示set中不存在x
q.clear();		//清空q
q.empty();		//判断q是否为空,若是返回1,否则返回0
q.size();		//返回q中元素的个数
q.find(x);		//在q中查找x,返回x的迭代器,若x不存在,则返回指向q尾部的迭代器即 q.end()
q.lower_bound(x); //返回一个迭代器,指向第一个键值不小于x的元素
q.upper_bound(x); //返回一个迭代器,指向第一个键值大于x的元素

q.rend();		  //返回第一个元素的的前一个元素迭代器
q.begin();		  //返回指向q中第一个元素的迭代器

q.end();		 //返回指向q最后一个元素下一个位置的迭代器
q.rbegin();		 //返回最后一个元素

std::tuple

std::tuple is a container that allows grouping multiple values of different types together as a single object. It is a part of the C++11 standard library and is a generalization of std::pair . A tuple can store any number of objects, and each object can have its own type.

C++中的元组,类似于泛化的std::pair,可以当成一个结构体来用。

来自 https://en.cppreference.com/w/cpp/utility/tuple 的例子:

std::tuple<int, int> foo_tuple()
{
    return {1, -1};  // Error until N4387
    return std::tuple<int, int>{1, -1}; // Always works
    return std::make_tuple(1, -1); // Always works
}

#include <iostream>
#include <stdexcept>
#include <string>
#include <tuple>
 
std::tuple<double, char, std::string> get_student(int id)
{
    switch (id)
    {
        case 0: return {3.8, 'A', "Lisa Simpson"};
        case 1: return {2.9, 'C', "Milhouse Van Houten"};
        case 2: return {1.7, 'D', "Ralph Wiggum"};
        case 3: return {0.6, 'F', "Bart Simpson"};
    }
 
    throw std::invalid_argument("id");
}
 
int main()
{
    const auto student0 = get_student(0);
    std::cout << "ID: 0, "
              << "GPA: " << std::get<0>(student0) << ", "
              << "grade: " << std::get<1>(student0) << ", "
              << "name: " << std::get<2>(student0) << '\n';
 
    const auto student1 = get_student(1);
    std::cout << "ID: 1, "
              << "GPA: " << std::get<double>(student1) << ", "
              << "grade: " << std::get<char>(student1) << ", "
              << "name: " << std::get<std::string>(student1) << '\n';
 
    double gpa2;
    char grade2;
    std::string name2;
    std::tie(gpa2, grade2, name2) = get_student(2);
    std::cout << "ID: 2, "
              << "GPA: " << gpa2 << ", "
              << "grade: " << grade2 << ", "
              << "name: " << name2 << '\n';
 
    // C++17 structured binding:
    const auto [gpa3, grade3, name3] = get_student(3);
    std::cout << "ID: 3, "
              << "GPA: " << gpa3 << ", "
              << "grade: " << grade3 << ", "
              << "name: " << name3 << '\n';
}

其他例子:

// 创建
tuple<int,float,int,float> tu = make_tuple(1,2.f,3,4.f);        //创建方式一
tuple<int,float,int,float> tu(1,2.f,3,4.f);                     //创建方式二

//相当于结构体:
struct tu
{
   int a;
   float b;
   int c;
   float d;
}
// 以上创建方式只是实参的拷贝,如果我们修改这些参数是无法真正修改实际的参数的值

// 以下是直接修改参数的方式
int a = 0;
float b = .f;
int c = 0;
float d = .1;

auto tu = tie(a,b,c,d);

//本地进行修改
get<0> (tu) = 2;
get<1> (tu) = 4.5f;
get<2> (tu) = 234;
get<2> (tu) = 22.f;

//当然还可以这么用
auto tu1 = make_tuple(1,2.f,3,4.f);



std::string str_five_1("five_1");
// 输出原址值
std::cout << "str_five_1 = " << str_five_1.c_str() << "\n";

std::tuple<std::string&, int> five(str_five_1, 5);
// 通过元组 对第一个元素的修改,str_five_1的值也会跟着修改,因为元组的第一个元素类型为引用。
// 使用get访问元组的第一个元素
std::get<0>(five) = "five_2";

// 输出的将是: five_2
std::cout << "str_five_1 = " << str_five_1.c_str() << "\n";

std::tuple<char, int, long, std::string> fourth('A', 2, 3, "4");

// 元组,可以看作一个包,类比结构体。 需要访问元组的元素时,2 种方法: A、索引访问,B、std::tie 。
// 元组包含一个或者多个元素,使用std::tie解包: 首先需要定义对应元素的变量,再使用tie。 比如,元素第0个元素的类型时 char, 第1个元素类型时int,  那么, 需要定义 一个 char的变量和int的变量, 用来储存解包元素的结果。 

// 定义变量,保存解包结果
char tuple_0    = '0';
int tuple_1        = 0;
long tuple_2    = 0;
std::string tuple_3("");

// 使用std::tie, 依次传入对应的解包变量
std::tie(tuple_0, tuple_1, tuple_2, tuple_3) = fourth;

// 输出解包结果
std::cout << "tuple_0 = " << tuple_0 << "\n";
std::cout << "tuple_1 = " << tuple_1 << "\n";
std::cout << "tuple_2 = " << tuple_2 << "\n";
std::cout << "tuple_3 = " << tuple_3.c_str() << "\n";

std::accumulate

#include <iostream>
#include <vector>
#include <numeric>
 
int main( int argc, char **argv )
{
    std::vector<int> v{1, 2, 3, 4};
 
    int v1 = std::accumulate( v.begin(), v.end(), 0 );
    int v2 = std::accumulate( v.begin(), v.end(), 1, std::multiplies<int>() );
    int v3 = std::accumulate( v.begin(), v.end(), 1, std::multiplies<int>{} );
 
    std::cout << "v1: " << v1 << '\n';
    std::cout << "v2: " << v2 << '\n';
    std::cout << "v3: " << v3 << '\n';
    return 0;
}

// 输出
// v1: 10
// v2: 24
// v3: 24

参考: