【STL】STL 操作
目录
简介:
标准STL序列容器知:vector、string、deque和道list。
标准STL关联容器:set、专multiset、map和multimap。
非标准序列容器slist和rope。slist是一个单向链表,属rope本质上是一“重型”string。
非标准的关联容器hash_set、hase_multiset、hash_map和hash_multimap。 vector<char> 作为string的替代。
map, set, multimap, and multiset四种容器采用红黑树实现,红黑树是平衡二叉树的一种。
不错的博文:https://www.cnblogs.com/isChenJY/category/1548486.html
1、vector:
加头文件#include <vector>
1.2 vector初始化:
vector<int>a(10); //定义具有10个整型元素的向量不具有初值,其值不确定
vector<int>a(10,1);//定义具有10个整型元素的向量,且给出的每个元素初值为1
vector<int>a(b); //用向量b给向量a赋值,a的值完全等价于b的值
vector<int>a(b.begin(),b.begin+3); //将向量b中从0-2(共三个)的元素赋值给a,a的类型为int型
int b[7]={1,2,3,4,5,6,7};
vector<int> a(b,b+7); //从数组中获得初值
1.3 vector对象的常用内置函数使用(举例说明)
#include<vector>
vector<int> a, b;
//返回a的最后一个元素
a.back();
//返回a的第一个元素
a.front();
//返回a的第i元素,当且仅当a存在
a[i];
//清空a中的元素
a.clear();
//判断a是否为空,空则返回true,非空则返回false
a.empty();
//删除a向量的最后一个元素
a.pop_back();
//在a的最后一个向量后插入一个元素,其值为5
a.push_back(5);
//返回a中元素的个数
a.size();
//返回a在内存中总共可以容纳的元素个数
a.capacity();
//b为向量,将a中的元素和b中的元素整体交换
a.swap(b);
//b为向量,向量的比较操作还有 != >= > <= <
a == b;
//b为向量,将b的0-2个元素赋值给向量a
a.assign(b.begin(), b.begin() + 3);
//a含有4个值为2的元素
a.assign(4, 2);
//删除a中第一个(从第0个算起)到第二个元素,也就是说删除的元素从a.begin()+1算起(包括它)一直到a.begin()+3(不包括它)结束
a.erase(a.begin() + 1, a.begin() + 3);
//在a的第一个元素(从第0个算起)位置插入数值5,
a.insert(a.begin() + 1, 5);
//在a的第一个元素(从第0个算起)位置插入3个数,其值都为5
a.insert(a.begin() + 1, 3, 5);
//b为数组,在a的第一个元素(从第0个元素算起)的位置插入b的第三个元素到第5个元素(不包括b+6)
a.insert(a.begin() + 1, b + 3, b + 6);
//将a的现有元素个数调整至10个,多则删,少则补,其值随机
a.resize(10);
//将a的现有元素个数调整至10个,多则删,少则补,其值为2
a.resize(10, 2);
//将a的容量扩充至100,
a.reserve(100);
5.常见错误赋值方式
vector<int>a;
for(int i=0;i<10;++i){a[i]=i;}//下标只能用来获取已经存在的元素
3.几个常用的算法
#include<algorithm>
//对a中的从a.begin()(包括它)到a.end()(不包括它)的元素进行从小到大排列
sort(a.begin(),a.end());
//对a中的从a.begin()(包括它)到a.end()(不包括它)的元素倒置,但不排列,如a中元素为1,3,2,4,倒置后为4,2,3,1
reverse(a.begin(),a.end());
//把a中的从a.begin()(包括它)到a.end()(不包括它)的元素复制到b中,从b.begin()+1的位置(包括它)开始复制,覆盖掉原有元素
copy(a.begin(),a.end(),b.begin()+1);
//在a中的从a.begin()(包括它)到a.end()(不包括它)的元素中查找10,若存在返回其在向量中的位置
find(a.begin(),a.end(),10);
原文链接:https://blog.csdn.net/weixin_41743247/article/details/90635931
2、string
加头文件#include <string>
string初始化:
string str1; //生成空字符串
string str2("123456789"); //生成"1234456789"的复制品
string str3("12345", 0, 3);//用字符串0-3的字节初始化string结果为"123"
string str4("012345", 5); //用字符串前5个字节初始化string结果为"01234"
string str5(5, '1'); //生成5个'1'字节的string,结果为"11111"
string str6(str2, 2); //用str2从2地址开始到结束的字节初始化string,结果为"3456789"
string对象的常用内置函数使用(举例说明)
string s("1234567");
s.size() //返回对象字符个数
s.length() //返回对象字符个数
s.max_size() //返回string对象最多包含的字符数
s.capacity() //重新分配内存之前,string对象能包含的最大字符数
比较compare
string A("aBcd");
string B("Abcd");
string C("123456");
string D("123dfg");
0:相等 1:大于 -1:小于
//"aBcd"和"Abcd"比较------ a > A
A.compare(B) // 结果:1
//"cd" 和 "Abcd"比较------- c > A
A.compare(2, 3, B) // 结果:1
// "cd" 和 "cd"比较
A.compare(2, 3, B, 2, 3)// 结果:0
// 由结果看出来:0表示下标,3表示长度
//"123" 和 "123"比较
C.compare(0, 3, D, 0, 3) // 结果:0
遍历
string s1("abcdef"); // 调用一次构造函数
// 方法一: 下标法
for( int i = 0; i < s1.size() ; i++ ){
cout<<s1[i];
}
cout<<endl;
// 方法二:正向迭代器
string::iterator iter = s1.begin();
for( ; iter < s1.end() ; iter++){
cout<< *iter << " ";
}
cout<<endl;
拼接字符串
string s1("abc");
string s2 = "abc";
string s3 = "def";
s1.append("def"); //s1:abcdef
s1 += "def"; //或者直接用 操作符+ s1:abcdef
s2 += s3.c_str(); //s2:abcdef
其他
查找:
//pos索引位置开始,查找子串s,返回找到的位置索引, -1表示查找不到子串
size_t find(constchar* s, size_t pos = 0) const;
string s("dog bird chicken bird cat");
s.find("chicken") // 结果是:9
s.find('i', 6) // 结果是:11
s.rfind("chicken") // 反向查找,还是首字母在字符串中的下标:9
替换:
string s1("hello,world!");
/ 这里的6表示下标 5表示长度
s1.replace(6,5,"girl"); // 结果:hello,girl.
删除:
string s1 = "123456789";
s1.erase(1,6); // 结果:189
插入:
string s1("ab");
s1.push_back('c'); // s1:abc
转换:
tolower()和toupper()
分割/截取字符串:
char str[] = "I,am,a,student; hello world!";
char *p2 = strtok(str,split); //分割字符串
while( p2 != NULL )
{
cout<<p2<<endl;
p2 = strtok(NULL,split);
}
string s1("0123456789");
string s2 = s1.substr(2,5); // 结果:23456-----参数5表示:截取的字符串的长度
// inserting into a string
#include <iostream>
#include <string>
int main ()
{
std::string str="to be question";
std::string str2="the ";
std::string str3="or not to be";
std::string::iterator it;
// used in the same order as described above:
str.insert(6,str2); // to be (the )question
str.insert(6,str3,3,4); // to be (not )the question #在第6位置,插入str3的第三个位置开始4个字节
str.insert(10,"that is cool",8); // to be not (that is )the question
str.insert(10,"to be "); // to be not (to be )that is the question
str.insert(15,1,':'); // to be not to be(:) that is the question
it = str.insert(str.begin()+5,','); // to be(,) not to be: that is the question
str.insert (str.end(),3,'.'); // to be, not to be: that is the question(...)
str.insert (it+2,str3.begin(),str3.begin()+3); // (or )
std::cout << str << '\n';
return 0;
}
更多见:https://blog.csdn.net/dbsggal90047018/article/details/101609394
https://www.cnblogs.com/eilearn/p/10990285.html
3、set
set的特性是:所有元素都会根据元素的键值自动排序,set的元素不像map那样可以同时拥有实值(value)和键值(key),set元素的键值就是实值,实值就是键值。set不允许两个元素有相同的键值。
加头文件#include <set>
常用的函数
begin() 返回set容器的第一个元素的地址
end() 返回set容器的最后一个元素地址
clear() 删除set容器中的所有的元素
empty() 判断set容器是否为空
max_size() 返回set容器可能包含的元素最大个数
size() 返回当前set容器中的元素个数
erase(it) 删除迭代器指针it处元素
count() :用来查找set中某个元素出现的次数。一个键值在set只可能出现0或1次,成了判断某一键值是否在set出现过了。
find(): 用来查找set中某个元素出现的位置。如果找到,就返回这个元素的迭代器,如果这个元素不存在,则返回 s.end() 。
set<int> s;
s.insert(1);
s.insert(2);
s.insert(3);
s.insert(1);
cout << "set 的 size 值为 :" << s.size() << endl;
cout << "set 的 maxsize的值为 :" << s.max_size() << endl;
cout << "set 中的第一个元素是 :" << *s.begin() << endl;
cout << "set 中的最后一个元素是:" << *s.end() << endl;
//遍历数据,用迭代器遍历数据
for (set<int>::iterator it = s.begin(); it != s.end(); ++it)
{
cout << *it << endl;
}
it1 = st1.find(4); //查找数据
if (it1 != st1.end()) //如果找到就输出数据
{
cout << *it1 << endl;
}
s.clear();
if(s.empty())
{
cout << "set 为空 !!!" << endl;
}
cout << "set 的 size 值为 :" << s.size() << endl;
cout << "set 的 maxsize的值为 :" << s.max_size() << endl;
结构体类型(struct )的set ,使用时必须要重载 '<' 运算符
#include<iostream>
#include<set>
#include<string>
using namespace std;
struct Info
{
string name;
double score;
bool operator < (const Info &a) const // 重载“<”操作符,自定义排序规则
{
//按score由大到小排序。如果要由小到大排序,使用“>”即可。
return a.score < score;
}
};
int main()
{
set<Info> s;
Info info;
//插入三个元素
info.name = "Jack";
info.score = 80;
s.insert(info);
info.name = "Tom";
info.score = 99;
s.insert(info);
info.name = "Steaven";
info.score = 60;
s.insert(info);
set<Info>::iterator it;
for(it = s.begin(); it != s.end(); it++)
cout << (*it).name << " : " << (*it).score << endl;
return 0;
}
/*
运行结果:
Tom : 99
Jack : 80
Steaven : 60
*/
4、map
https://blog.csdn.net/bandaoyu/article/details/83312118
map 需要确定映射前类型(键key)和映射后类型(值value) //其中第一个是键的类型,第二个是值的类型
map<typename1, typename2> mp;
map<string, int> mp; //必须使用string而不能用char数组
map<set<int>, string> mp; //map的键和值也可以是STL容器//将一个 set 容器映射到一个字符串
map<char, int> mp;
mp['c'] = 20;
mp['c'] = 30; //20被覆盖
mp['m'] = 20;b
mp['r'] = 30;
mp['a'] = 40;
mp['b'] = 40;
for(map<char, int>::iterator it = mp.begin(); it != mp.end(); it++)
printf("%c %d\n", it -> first, it -> second);
map<char, int>::iterator it = mp.find('b');
printf("%c %d\n", it -> first, it -> second);
mp.erase(it); //删除 b 2
//mp.erase(it. mp.end()); //删除it之后的所有映射,即b 2和c 3
mp.erase('r'); //删除键为b的映射,即 b 2
mp.size()
mp.clear(); //清空map
5、list
vector:随机访问效率高,但随机插入效率低
list:随机访问效率低,但随机插入效率高
常用
#include <list> // list属于std命名域的,因此需要通过命名限定,例如using std::list;
list<int> a; // 定义一个int类型的列表a
list<int> a(10); // 定义一个int类型的列表a,并设置初始大小为10
list<int> a(10, 1); // 定义一个int类型的列表a,并设置初始大小为10且初始值都为1
list<int> b(a); // 定义并用列表a初始化列表b
deque<int> b(a.begin(), ++a.end()); // 将列表a中的第1个元素作为列表b的初始值
除此之外,还可以直接使用数组来初始化向量:
int n[] = { 1, 2, 3, 4, 5 };
list<int> a(n, n + 5); // 将数组n的前5个元素作为列表a的初值
lst.size() 容器大小
lst.max_size() 容器最大容量
lst.resize() 更改容器大小
lst.empty() 容器判空
lst.front() 访问第一个元素
lst.back() 访问最后一个元素
lst.push_front(const T& x) 头部添加元素
lst.push_back(const T& x) 末尾添加元素
lst.insert(iterator it, const T& x) 任意位置插入一个元素
lst.insert(iterator it, int n, const T& x) 任意位置插入 n 个相同元素
lst.insert(iterator it, iterator first, iterator last) 插入另一个向量的 [forst,last] 间的数据
lst.pop_front() 头部删除元素
lst.pop_back() 末尾删除元素
lst.erase(iterator it) 任意位置删除一个元素
lst.erase(iterator first, iterator last) 删除 [first,last] 之间的元素:;
lst.clear() 清空所有元素:;
多个元素赋值:lst.assign(int nSize, const T& x); // 类似于初始化时用数组进行赋值
交换两个同类型容器的元素:swap(list&, list&); 或 lst.swap(list&);
合并两个列表的元素(默认升序排列):lst.merge();
在任意位置拼接入另一个list:lst.splice(iterator it, list&);
删除容器中相邻的重复元素:lst.unique();
#include <iostream>
#include <list>
using namespace std;
int main(int argc, char* argv[])
{
list<int> lst;
for (int i = 0; i<6; i++)
{
lst.push_back(i);
}
cout << lst.size() << endl; // 输出:6
cout << lst.max_size() << endl; // 输出:357913941
lst.resize(0); // 更改元素大小
cout << lst.size() << endl; // 输出:0
if (lst.empty())
cout << "元素为空" << endl; // 输出:元素为空
return 0;
}
算法
遍历元素
list<int>::iterator it;
for (it = lst.begin(); it != lst.end(); it++)
cout << *it << endl;
元素翻转
#include <algorithm>
reverse(lst.begin(), lst.end());
元素排序
#include <algorithm>
sort(lst.begin(), lst.end()); // 采用的是从小到大的排序
// 如果想从大到小排序,可以采用先排序后反转的方式,也可以采用下面方法:
// 自定义从大到小的比较器,用来改变排序方式
bool Comp(const int& a, const int& b)
{
return a > b;
}
sort(lst.begin(), lst.end(), Comp);
更详细:https://www.cnblogs.com/linuxAndMcu/p/10260627.html
6、array
array< T , N >:表示可以存储 N 个 T 类型的元素,T可以是任意类型的元素。此类容器一旦建立后,其长度便是固定不变的,不能增加或删除元素,而只能改变某个元素的值;
array和我们经常接触的数组别无二致,都无法增加和删除元素。
但它比数组更好用,和标准的数组相比,array 容器的额外幵销很小,同时提供了两个优点:如果使用 at(),当用一个非法的索引访问数组元素时,能够被检测到,因为容器知道它有多少个元素,这也就意味着数组容器可以作为参数传给函数,而不再需要单独去指定数组元素的个数。
定义
array<int,5> my_array1,只是定义了该array容器,没有为其指定初值,因此容器中的内容不是我们所预期的。
array<int,5> my_array2 {},使用该语句初始化array容器后,容器中所有的元素都是0。
array<int,5> my_array3 {1,2,3,4},该容器的前四个值被赋值,而第五个由于没有被赋值,默认为0。
array<int,5> my_array4 {}; my_array4.fill(2020); 该容器内所有的值被统一为2020。
访问
array<int,10> my_array1 {1,1};
for(int i=0;i<my_array1.size();i++){
cout<<my_array1[i]<<" ";
}
//为了防止访问越界,我们可以使用 at 方法
array<int,10> my_array2 {1,1};
for(int i=0;i<my_array1.size();i++){
my_array2.at(i+2)=my_array2.at(i+1)+my_array2.at(i);
}
https://www.jianshu.com/p/8f58bf0752af
queue
应先添加头文件#include <queue>,
并在头文件下面加上"using namespace std;",即可使用。
//定义写法
queue< typename > name; //typename 可以是任意基本数据类型或容器
queue<int> q;
for(int i = 1; i <= 5; i++)
{
q.push(i); //依次入队1 2 3 4 5
}
for(int i = 1; i <= 3; i++)
{
q.pop(); //出队首元素三次(即依次出队1 2 3)
}
printf("%d\n", q.front()));
if(q.empty() == true) { //一开始队列内没有元素,所以是空
q.size()); //队列中有5个元素
queue的常见用途
需要实现广度优先搜索时,可以不用自己手动实现一个队列,而是用queue作为替代。
https://www.cnblogs.com/isChenJY/p/11601523.html
deque
双端队列
底层是一个双向链表。
常用的有队列的尾部入队、首部出队。
https://www.cnblogs.com/ayaoyao/p/9552920.html
stack
//要使用stack,应先添加头文件
#include <stack>,
并在头文件下面加上"using namespace std"
//定义
stack< typename > name;
stack<int> st;
for(int i = 1; i <= 5; i++)
{
st.push(i); //push(i)用以把i压入栈,故此处依次入栈 1 2 3 4 5
}
st.pop(); //栈顶元素出栈
st.top(); //top取栈顶元素
if(st.empty() == true) //栈内没有元素?
stack的常见用途
模拟实现一些递归,防止程序堆栈内存的现在而导致程序运行出错
pair
//pair是一个很实用的”小玩意“,当想要将两个元素绑在一起作为一个合成元素,
//又不想因此定义结构体时,使用pair可以很方便地作为一个替代品。
//也就是说,pair实际上可以看作一个内部有两个元素地结构体,
//且这两个元素的类型是可以指定的。
struct pair {
typename1 first;
typename2 second;
};
//要使用pair,应先添加头文件#include <utility>,并在头文件下面加上"using namespace std"
//注意:由于map的内部实现中涉及pair,因此添加map头文件时会自动添加utility头文件
//此时如果需要使用pair,就不需要额外再添加utility头文件
//pair有两个参数,分别对应first和second的数据类型,它们可以是任意基本数据类型或容器
//定义
pair<typeName1, typeName2> name;
//定义参数为string和int类型的pair
pair<string, int> p;
//初始化
pair<string, int> p("haha", 5);
//临时构建一个pair,有两种方法
//<1> 将类型定义写在前面,后面用小括号内两个元素的方式
pair<string, int> ("haha", 5)
//<2> 使用自带的make_pair函数
make_pair("haha", 5)
pair中元素的访问
pair<string, int> p;
p.first = "haha";
p.second = 5;
p = make_pair("xixi", 55);
p = pair<string, int>("heihei", 555);
pair的常见用途
用来提代二元结构体及其构造函数,可以节省编码时间
作为map的键值对来进行插入
#include <iostream>
#include <string>
#include <map>
using namespace std;
int main()
{
map<string, int> mp;
mp.insert(make_pair("heihei", 5));
mp.insert(pair<string, int>("haha", 10));
for(map<string, int>::iterator it = mp.begin(); it != mp.end(); it++)
{
cout << it -> first << " " << it -> second << endl;
}
return 0;
}
priority_queue
优先队列是一种容器,它可以使得其第一个元素始终是它包含的最大元素,具体实现原理是堆排序。
它支持以下操作:
-
empty()
-
size()
-
top()
-
push()
-
pop()
在使用priority_queue之前,请包含头文件#include <queue>
#include<iostream>
#include<vector>
#include<algorithm>
#include <queue>
#include <functional>
using namespace std;
int main()
{
//大顶堆
priority_queue<int, vector<int>, less<int>> q;
//小顶堆
priority_queue<int, vector<int>, greater<int>> p;
for (int i = 0; i < 10; i++)
{
q.push(i);
p.push(i);
}
cout << "less<int>" << endl;
while (!q.empty())
{
cout << q.top() << " ";
q.pop();
}
cout << endl;
cout << "greater<int>" << endl;
while (!p.empty())
{
cout << p.top() << " ";
p.pop();
}
return 0;
}
C++11容器中新增加的emplace相关函数的使用
C++11中,针对顺序容器(如vector、deque、list),新标准引入了三个新成员:emplace_front、emplace和emplace_back,这些操作构造而不是拷贝元素。这些操作分别对应push_front、insert和push_back,允许我们将元素放置在容器头部、一个指定位置之前或容器尾部。
当调用push或insert成员函数时,我们将元素类型的对象传递给它们,这些对象被拷贝到容器中。而当我们调用一个emplace成员函数时,则是将参数传递给元素类型的构造函数。emplace成员使用这些参数在容器管理的内存空间中直接构造元素。
emplace函数的参数根据元素类型而变化,参数必须与元素类型的构造函数相匹配。emplace函数在容器中直接构造元素。传递给emplace函数的参数必须与元素类型的构造函数相匹配。
其它容器中,std::forward_list中的emplace_after、emplace_front函数,std::map/std::multimap中的emplace、emplace_hint函数,std::set/std::multiset中的emplace、emplace_hint,std::stack中的emplace函数,等emplace相似函数操作也均是构造而不是拷贝元素。
emplace相关函数可以减少内存拷贝和移动。当插入rvalue,它节约了一次move构造,当插入lvalue,它节约了一次copy构造。
下面是从其他文章中copy的测试代码,详细内容介绍可以参考对应的reference:
https://blog.csdn.net/fengbingchun/article/details/78670376
emplace_back/emplace等能通过参数直接在容器中构造对象,相比push_back/insert能更好避免内存的拷贝与移动。
emplace_back的函数原型为
template <class... Args>
void emplace_back (Args&&... args);
- 所有的标准库容器都增加了类似的方法emplace、emplace_hint、emplace_front、emplace_after和emplace_back。
- emplace能通过构造函数的参数就直接构造对象,如果没有相应的构造函数则会报错
可以通过下面的例子来看其基本的使用和特性
#include<iostream>
#include<map>
#include<vector>
using namespace std;
struct Test {
int a;
string b;
Test(int _a, string _b):a(_a), b(_b){
cout << "construct" << endl;
}
Test(const Test&& test):a(test.a), b(move(test.b)) {
cout << "move" << endl;
}
};
int main() {
map<int, Test> m;
int a = 0;
string b = "test";
cout << "--insert--" << endl;
m.insert(make_pair(4, Test(a, b)));
cout << "--emplace--" << endl;
m.emplace(4, Test(a, b)); //通过构造函数参数直接构造对象
vector<Test> v;
cout << "--push_back--" << endl;
v.push_back(Test(a,b));
cout << "--emplace_back--" << endl;
v.emplace_back(a, b);
}
其输出为
image.png
上面例子是在TDM-GCC 4.8中的运行结果。emplace比insert少用一次移动拷贝构造函数、push_back和emplace_back的运行情况相同。可以看出,emplace*的性能与STL的具体实现和编译器都有关系。不过一般情况下emplace(或emplace_back等)的性能要好于insert(或push_back等)的性能。关于emplace实现可能存在的问题可参考https://stackoverflow.com/questions/4303513/push-back-vs-emplace-back
作者:_gentle
链接:https://www.jianshu.com/p/caad33287dc3
来源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。