首页 > 其他分享 >STL 容器用法简要整理(未完成)

STL 容器用法简要整理(未完成)

时间:2024-10-11 21:22:11浏览次数:6  
标签:简要 string v1 STL 元素 42 用法 int vector

STL 容器用法简要整理

本文将简要介绍 C++14 中可以使用的 STL 容器的用法。根据 CCF 规定,这些容器都可以在比赛中使用。

本文中的代码均为 C++14。

本文中的代码均已引入了相关的库,并 using namespace std

共同点

  • 特性:所有能用下标访问的 STL 容器,下标都是从 0 开始,到 size - 1 结束,如 vectorarraystring

  • 初始化:STL 容器的初始化都是形如 container<T> name 的形式,其中 T 是数据类型,如 intchar,结构体或其它 STL 容器;name 是你起的容器的名字。例如,创建一个空的 intvector 可以这么写:vector<int> v

  • =:赋值运算符。可以把某个容器的值赋值给另一个同类容器。以 vector 为例:

    int main()
    {
    	vector<int> v1{1, 2, 3, 4, 5};
    	vector<int> v2 = v1;
        // 两个容器的类型必须相同。vector<int> 和 vector<long long> 也是不能互相赋值的。
    	for(int x: v2) cout << x << ' ';
    	cout << endl; 
    	return 0;
    }
    

    输出:1 2 3 4 5

  • size():返回容器内的元素个数。(如无特别说明,提到的函数都是成员函数。)

  • empty()bool 型,返回容器是否为空。

  • swap()a.swap(b) 表示交换容器 a 和容器 b 的值。时间复杂度常数。但是有一个例外:arrayswap() 是线性的!(参见此处。作为对比,vectormap 的时间复杂度都是常数(constant)。)

    int main()
    {
    	vector<int> v1{1, 2, 3, 4, 5};
    	vector<int> v2{6, 7, 8, 9, 10};
    	v1.swap(v2);
    	cout << "v1: "; for(int x: v1) cout << x << ' '; cout << endl; 
    	cout << "v2: "; for(int x: v2) cout << x << ' '; cout << endl; 
    	return 0;
    }
    

    输出:

    v1: 6 7 8 9 10
    v2: 1 2 3 4 5
    
  • > / >= / < / <= / == / !=:按字典序比较两个容器。无序容器(如 unordered_map)只支持 ==!=

    int main()
    {
    	vector<int> v1{1, 2, 3, 4, 5}, v2{1, 2, 4, 2, 1}, v3{1, 2, 3, 4, 5, 6};
    	cout << "v1 < v2: " << boolalpha << (v1 < v2) << endl;
    	cout << "v1 < v3: " << boolalpha << (v1 < v3) << endl;
    	string s1("cat"), s2("cat"), s3("cap");
    	cout << "s1 == s2: " << boolalpha << (s1 == s2) << endl;
    	cout << "s1 < s3: " << boolalpha << (s1 < s3) << endl;
    	return 0;
    }
    

    输出:

    v1 < v2: true
    v1 < v3: true
    s1 == s2: true
    s1 < s3: false
    
  • 迭代器(iterator):迭代器类似指针。用 * 可以解引用。

    声明时可以用 autoauto it = v1.begin(),也可以用 container::iteratorvector<int>::iterator it

    begin() 返回指向容器开头的迭代器,end() 返回指向容器最后一个元素的后继的迭代器。(所以实际上 end() 不指向任何一个元素,元素的地址范围是 [begin(), end())。)

    对于 vectorarray 等可以用下标访问的容器,可以用 *(it + c) 的形式来访问元素,其中 it 是迭代器,c 是整数。也可以用这种形式修改元素,就像指针一样。这里以 array 为例:

    int main()
    {
    	array<int, 5> a{1, 2, 3, 4, 5};
    	auto it = a.begin(), it2 = a.end();
    	cout << *it << ' ' << *(it + 1) << ' ' << *(it2 - 1) << endl;
    	*it = 2, *(it + 1) = 10;
    	for(int x: a) cout << x << ' '; cout << endl; 
    	return 0;
    }
    

    输出:

    1 2 5
    2 10 3 4 5
    

    更多关于迭代器的知识,参见此处

vector(向量)

std::vector - cppreference.com

可变长度数组。解决了 C 风格数组长度只能是常数的问题。它支持如下操作:\(O(1)\) 的随机访问,\(O(n)\) 的插入/删除元素。

我们经常能看到这样的题目(尤其是在 CF 上):有一个 \(n \times m\) 的矩阵,我们要存储每个元素的信息,而 \(nm \le 10^6\),但对于 \(n\),\(m\) 的大小没有单独限定。这时候用数组就不能保证既不爆空间,又能存下所有数据。vector 的作用就体现出来了。

注意,不要使用 vector<bool>vector<bool> 不是 vector,使用它可能会出现意外的错误。如必须使用,可以用 vector<char> 代替,二者空间相同。

需要注意的是,vector 的常数有时劣于数组(待验证)。

  • 初始化vector 的初始化方式有许多种,使用合理的方式可以为我们提供便利。参见此处

    下面是 5 种常用的初始化方法:

    #define vec_print(x) cout << #x": ", print(x)
    void print(vector<int> &v)
    {
    	for(int x: v) cout << x << ' ';
    	cout << endl; 
    }
    
    int main()
    {
    	vector<int> v1; // 1. 创建空 vector,时间复杂度常数
    	int n = 5;
    	vector<int> v2(n); // 2. 创建长度为n的vector,时间复杂度线性。
    	// 元素的初值都为0。下面会讨论这个初值是怎么得来的。 
    	// 这体现了与数组的不同之处:长度可以是变量。
    	vector<int> v3(n, 42); // 3. 创建长度为n的vector,每个元素都是42。时间复杂度线性。
    	vector<int> v4(v3); // 4. 创建一个与v3相同的vector。时间复杂度与v3的大小线性相关。
    	vector<int> v5(v4.begin() + 1, v4.end() - 1);
    	// 5. 把[v4[1] ~ v4[4])中的元素拷贝到v5中(注意是左闭右开区间!) 
    	// 时间复杂度和拷贝的元素数量线性相关。 
        vector<int> v6{1, 2, 3, 4, 5}; // 6. 用列表初始化,时间复杂度? 
        // (我不知道这个方法的时间复杂度,但是元素太多的时候一般不会用列表初始化,因此时间可以忽略不计)
    	
    	vec_print(v1), vec_print(v2), vec_print(v3), vec_print(v4), vec_print(v5), vec_print(v6);
    	
    	return 0;
    }
    

    输出:

    v1:
    v2: 0 0 0 0 0
    v3: 42 42 42 42 42
    v4: 42 42 42 42 42
    v5: 42 42 42
    v6: 1 2 3 4 5
    

    关于方法 2 中,元素的初值:

    好吧,我没有完全搞懂。cppreference 上的原文表示元素的值是 default-inserted instance of T,但我不知道什么叫 "default-inserted"。我猜测应该是元素默认的值:例如整型默认是 0

    如果元素类型是结构体,而结构体有构造函数,那么元素的初值通过构造函数得来:

    struct Node
    {
    	int x;
    	Node(): x(42) {}
    };
    
    int main()
    {
    	vector<Node> v(5);
    	for(auto nd: v) cout << nd.x << ' ';
    	cout << endl;
    	return 0;
    }
    

    输出:42 42 42 42 42

    关于方法 5:

    用某个 vector 中一段元素的值初始化另一个 vector,两个 vector 的元素类型可以不相同,但要满足可以互相转换。例如 int 可以转成 long long。或者,如果被初始化的 vector 的元素是结构体,要有对应的构造函数。

    需要注意的是,如果两个 vector 的元素类型不同,就不能用 vector<int> v2(v1) 之类的形式来初始化,即使两种元素类型可以互相转换或者有构造函数也不行。

    struct Node
    {
    	string str;
    	Node(int x): str(to_string(x * 10) + "str") {}
    };
    
    int main()
    {
    	vector<int> v1{1, 2, 3, 4, 5};
    	vector<long long> v2(v1.begin(), v1.end()); // ok,int 和 long long 可以转换 
    //	vector<string> v3(v1.begin(), v1.end()); // wrong,int 不能转成 long long 
    	vector<Node> v4(v1.begin(), v1.end()); // ok,存在 int 到 Node 的构造函数 
    //	vector<Node> v5(v1); // wrong,不同元素类型的 vector 不能互相初始化
        
    	cout << "v1: "; for(auto x: v1) cout << x << ' '; cout << endl;
    	cout << "v2: "; for(auto x: v2) cout << x << ' '; cout << endl;
    	cout << "v4: "; for(auto x: v4) cout << x.str << ' '; cout << endl;
    	return 0;
    }
    

    输出:

    v1: 1 2 3 4 5
    v2: 1 2 3 4 5
    v4: 10str 20str 30str 40str 50str
    

以下是一些常用的成员函数(STL 共有的成员函数不再列出):

  • 访问元素的方式:

    1. 通过 [] 访问。
    2. 通过 at() 访问。与前者的区别是用 at 访问时会检测有没有越界。at() 的效率低于 [],所以一般情况下都用 [],但是如果觉得自己可能会 RE,可以用 at() 以便于调试。
    3. front() 访问首元素,back() 访问尾元素。(注意与 begin()end() 区分,它们是迭代器。)
    4. 用迭代器访问:*it 表示 it 指向的元素的值,例如*begin() 就表示首元素的值。
    int main()
    {
    	vector<int> v{1, 2, 3, 4, 5};
    	cout << v.front() << ' ' << v[1] << ' ' << v.at(2) << ' ' << *(v.begin() + 3) << ' ' << v.back() << endl;
    	return 0;
    }
    

    输出:1 2 3 4 5

    at() 访问时,如果越界,会输出错误信息。

    int main()
    {
    	vector<int> v{1, 2, 3, 4, 5};
    	cout << v.at(5) << endl;
    	return 0;
    }
    

    输出:

    terminate called after throwing an instance of 'std::out_of_range'
      what():  vector::_M_range_check: __n (which is 5) >= this->size() (which is 5)
    
  • 添加元素:push_back():向 vector 的末尾增加元素。这么做会增加 vector 的长度。注意我们没有 push_front(),要实现类似操作,得用 deque

  • 删除元素:pop_back()。同理,没有 pop_front()

  • 改变 vector 的大小:resize()resize(n) 表示把大小改为 n。如果原先的长度大于 n,会删除多余的元素;否则:

    • 如果调用 resize(n),会在末尾添加元素的默认值;
    • 如果调用 resize(n, val),会在末尾补上 val

    时间复杂度和 nvector 原来大小的差线性相关。

    int main()
    {
    	vector<int> v{1, 2, 3, 4, 5};
    	v.resize(3);
    	for(int x: v) cout << x << ' '; cout << endl;
    	v.resize(10, 42);
    	for(int x: v) cout << x << ' '; cout << endl;
    	cout << "v.size() is " << v.size() << endl;
    	return 0;
    }
    

    输出:

    1 2 3
    1 2 3 42 42 42 42 42 42 42
    v.size() is 10
    
  • assign():给 vector 填充某个元素,有点像 fill()。用法:

    1. assign(n, val):把 vector 替换nval。时间复杂度 \(O(n)\)。vector 的大小多退少补。“替换”的意思是会覆盖原有的元素。
    2. 咕咕咕
  • inserterase():插入删除操作。通常情况下不使用这个函数,因为时间复杂度是线性。如果要做到常数的插入和删除,应该使用链表。这里不做介绍。

array(数组)

std::array - cppreference.com

定长数组。和 C 中的数组一样,长度必须为常数,效率优于 vector,和 C 中的数组相当。个人建议用 array 代替所有的定长数组,用 vector 代替所有的不定长数组。

大部分使用方法与 vector 相同,除了不能加入/删除末尾元素(push_back()/pop_back()),当然也不能 insert()erase()

下面是一些(个?)特有的函数:

  • fill()fill(x) 表示把 array 全部填充为 x。时间复杂度与 array 大小线性相关。

    int main()
    {
    	array<char, 5> a{'a', 'a', 'a', 'a', 'a'};
    	a.fill('b');
    	for(char ch: a) cout << ch; cout << endl;
    	return 0;
    }
    

    输出:bbbbb(覆盖了原先的值)

deque(双端队列/双端数组)

std::deque - cppreference.com

咕咕咕

string(字符串)

(严格来说 string 不算 container,但它很重要,这里一并整理用法。)

在 C 中,字符串是通过 char 数组实现的。而在 C++ 中,我们终于有了原生的字符串类型——string。它自带许多高效的函数,可以为我们写题(特别是字符串相关的模拟题)带来极大便利。与此同时,它的常数也十分优秀。

  • 初始化

    int main()
    {
    	string s1; // 1. 创建空字符串
    	string s2("test"); // 2. 创建特定字符串
    	int n = 5;
    	string s3(n, '='); // 3. 创建含n个'='的字符串,时间复杂度线性
    	auto print = [&](string name, string str) {cout << name << ": " << str << endl;};
    	print("s1", s1), print("s2", s2), print("s3", s3);
    	return 0;
    }
    

    输出:

    s1:
    s2: test
    s3: =====
    
  • find():查找某个子串/字符。如果存在该子串,返回第一个子串的第一个字符的下标;否则,返回 string::npos(本质上是一个 size_t 型的数)。

    int main()
    {
    	string str("This is a string."); /*
    	              ^  ^         ^
    	              2  5         15     */
    	cout << str.find("This") << ' ' << str.find("is") << endl;
    	cout << str.find('g') << endl; // 也可以找 char 
    	cout << str.find("is", 3) << endl; // find(s, pos):从下标pos处寻找s 
    	cout << str.find("1234") << ' ' << boolalpha << (str.find("1234") == string::npos) << endl; // 找不到示例 
    	return 0;
    }
    

    输出:

    0 2
    15
    5
    18446744073709551615 true
    

    其中 18446744073709551615 就是 string::npos,具体的值由编译器决定。

  • substr()

标签:简要,string,v1,STL,元素,42,用法,int,vector
From: https://www.cnblogs.com/dengstar/p/18459366

相关文章

  • 搜狗输入法ng版导入细胞词库过程的简要分析
    今天有点时间,对deepin/uos上的搜狗输入法ng版导入细胞词库的行为做了一下分析,过程如下:1.在属性设置界面,用户选择.scel细胞词库文件,输入法对.scel的文件头进行验证,如果是 401500004443530101,则验证通过,进行下一步操作。然而,在Windows下导入txt文件生成的细胞词库的文件......
  • ModelMapper的常见用法 ,号称是beanUtils.copyProp....的升级版??,代码复制粘贴即可复现效
    官网案例以下将官网案例做一个解释1)快速入门递归遍历源对象的属性拷贝给目标对象拷贝对象下对象的属性值@DataclassOrder{privateCustomercustomer;privateAddressbillingAddress;}@DataclassCustomer{privateNamename;}@Dataclass......
  • Python中key参数的含义及用法
    我们在使用sorted()或map()函数的时候,都会看到里面有一个key参数其实这个key参数也存在于其他内置函数中(例如min()、max()等),那么我们今天就来了解一下key参数的含义以及用途吧!sorted()中的key我们来看下面这段代码:some_numbers=[3.14159,2.71828,......
  • C# await 高级用法
    本文告诉大家await的高级用法,包括底层原理。昨天看到太子写了一段代码,我开始觉得他修改了编译器,要不然下面的代码怎么可以编译通过await"林德熙逗比";需要知道,基本可以添加await都是可以等待的类型,如Task。如果一个类需要可以被等待,那么这个类必须满足以下条件类里有一个Ge......
  • Oracle中alter table的常用用法
    首发微信公众号:SQL数据库运维原文链接:https://mp.weixin.qq.com/s?__biz=MzI1NTQyNzg3MQ==&mid=2247486440&idx=1&sn=b8a50ce5e993b4ab196ddda705077d95&chksm=ea375f98dd40d68ea079d90ac6084078e8ec9e1a4b1f4cc266fb97976dc2c72f452a61f55850&token=1175589249&la......
  • Curl一些基础用法
    这几天遇到一个很好用的工具,curl以下是curl的一些基础用法。url是一个非常强大的命令行工具,用于传输数据,支持多种协议,如HTTP、HTTPS、FTP等。以下是一些基本的curl语法和常用命令:基本语法curl[选项][URL...]常用选项-v,--verbose:详细模式,显示通信的整个过程。-s,--s......
  • STL——2.算法
    一、遍历1.for_eachvoidMyPrint(intval){cout<<val<<endl;}vector<int>v1={1,2,3,4};for_each(v1.begin(),v1.end(),MyPrint);2.transformv2.resize(v1.size());//先开辟空间,否则报错transform(v1,begin(),v1.end(),v2.begin(),MyPri......
  • STL——3.迭代器
     1.迭代器的基本概念作用:迭代器是用于遍历容器元素的对象。分类:输入迭代器输出迭代器前向迭代器双向迭代器随机访问迭代器2.迭代器的用法2.1输入迭代器#include<iostream>#include<iterator>#include<vector>intmain(){std::cout<<"Enterintegerssep......
  • 分布式系统1:什么是分布式系统——简要的介绍与定义
    写在前面本系列博文为博主在学习《高阶分布式系统》这门课的过程中写就。目的有二,第一是记录自己学习分布式系统的过程和心得,为后续从事分布式系统或者并行计算相关的研究打下较为夯实的基础。第二则是锻炼自己的逻辑与写作。本系列博文的写作目标不是教科书式一板一眼的教学,而......
  • 【C++】string (STL)
    string介绍字符串是表示字符序列的类标准的字符串类提供了对此类对象的支持,其接口类似于标准字符容器的接口,但添加了专门用于操作单字节字符字符串的设计特性。string类是使用char(即作为它的字符类型,使用它的默认char_traits和分配器类型(关于模板的更多信息,请参阅basic_s......