C++标准库常见用法

最近在刷leetcode的时候,经常要用到标准库,这里总结一下标准库的常见用法,用于查询使用。

queue

queue模板类的定义在<queue>头文件中。只能在容器的末尾添加新元素,只能从头部移除元素。

与stack模板类很相似,queue模板类也需要两个模板参数,一个是元素类型,一个容器类型,元素类型是必要的,容器类型是可选的,默认为deque类型。

定义queue对象的示例代码如下:

1
2
queue<int> q1;
queue<double> q2;

queue的基本操作有:

  1. 入队,如q.push(x); 将x接到队列的末端
  2. 出队,如q.pop(); 弹出队列的第一个元素,注意,并不会返回被弹出元素的值
  3. 访问队首元素,如q.front(); 即最早被压入队列的元素
  4. 访问队尾元素,如q.back(); 即最后被压入队列的元素
  5. 判断队列空,如q.empty(); 当队列空时,返回true
  6. 访问队列中的元素个数,如q.size();

vector

vector模板类的定义在<vector>头文件中。

定义vector对象的示例代码如下:

1
2
3
4
5
6
7
vector<int> v1;
vector<double> v2;
//初始化size,但每个元素值为默认值
vector<int> abc(10); //初始化了10个默认值为0的元素
//初始化size,并且设置初始值
vector<int> cde(101); //初始化了10个值为1的元素
vector<int> v{1,2,3,4,5};

vector的基本操作有:

  1. 尾部插入数字:vec.push_back(a);

  2. 使用下标访问元素:cout<<vec[0]<<endl; 记住下标是从0开始的。

  3. 使用迭代器访问元素:

    1
    2
    3
    vector<int>::iterator it;
    for(it=vec.begin();it!=vec.end();it++)
    cout<<*it<<endl;
  4. 插入元素:vec.insert(vec.begin()+i,a); 在第i个元素后面插入a;

  5. 删除元素:

    vec.erase(vec.begin()+2); 删除第3个元素;

    vec.erase(vec.begin()+i,vec.end()+j); 删除区间[i,j-1];区间从0开始

    vec.pop_back(); 删除最后一个元素

  6. 向量大小:vec.size();

  7. 得到首元素:vec[0]; 或者vec.front();

  8. 得到最后一个元素:vec[vec.size() - 1]; 或者vec.back();

  9. 清空:vec.clear()   //清空之后,vec.size()为0

  10. 反转:reverse(vec.begin(), vec.end());

stack

stack模板类的定义在<stack>头文件中。

定义stack对象的示例代码如下:

1
2
stack<int> s1;
stack<double> s2;

stack的基本操作有:

  1. top():返回一个栈顶元素的引用,类型为 T&。如果栈为空,返回值未定义。
  2. push(const T& obj):可以将对象副本压入栈顶。这是通过调用底层容器的 push_back() 函数完成的。
  3. push(T&& obj):以移动对象的方式将对象压入栈顶。这是通过调用底层容器的有右值引用参数的 push_back() 函数完成的。
  4. pop():弹出栈顶元素。
  5. size():返回栈中元素的个数。
  6. empty():在栈中没有元素的情况下返回 true。
  7. emplace():用传入的参数调用构造函数,在栈顶生成对象。
  8. swap(stack<T> & other_stack):将当前栈中的元素和参数中的元素交换。参数所包含元素的类型必须和当前栈的相同。对于 stack 对象有一个特例化的全局函数 swap() 可以使用。

map

map是一类关联式容器。提供一对一(其中第一个可以称为关键字,每个关键字只能在map中出现一次,第二个可能称为该关键字的值)的数据处理能力。

它的特点是增加和删除节点对迭代器的影响很小,除了那个操作节点,对其他的节点都没有什么影响。对于迭代器来说,可以修改实值,而不能修改key。

map定义在<map>头文件中。注意,STL头文件没有扩展名.h

定义map示例:

1
2
3
map<string, int>mapstring;         map<int, string >mapint;
map<sring, char>mapstring; map<char, string>mapchar;
map<char, int>mapchar; map<int, char>mapint;

插入数据:

1
2
3
4
map<int, string> maplive;  
maplive.insert(pair<int,string>(102,"aclive"));
maplive.insert(map<int,string>::value_type(321,"hai"));
maplive[112]="April";//map中最简单最常用的插入添加!

map中元素的查找: find()函数返回一个迭代器指向键值为key的元素,如果没找到就返回指向map尾部的迭代器。

1
2
3
4
5
6
7
8
map<int, string >::iterator l_it;; 
l_it = maplive.find(112);
if(l_it==maplive.end())
cout<<"we do not find 112"<<endl;
else {
l_it->second; // 注意这个地方使用的是->second
cout<<"wo find 112"<<endl;
}

map中元素的查找:count()函数,返回值为0或1,表示是否包含。

1
2
3
4
5
if(m.count(key)>0)
{
return m[key];
}
return null;

map中元素的删除:如果删除112;

1
2
3
4
5
6
map<int, string >::iterator l_it;;   
l_it = maplive.find(112);
if(l_it == maplive.end())
cout<<"we do not find 112"<<endl;
else
maplive.erase(l_it); //delete 112;

map的基本操作函数:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
C++ Maps是一种关联式容器,包含“关键字/值”对
begin() 返回指向map头部的迭代器
clear() 删除所有元素
count() 返回指定元素出现的次数
empty() 如果map为空则返回true
end() 返回指向map末尾的迭代器
equal_range() 返回特殊条目的迭代器对
erase() 删除一个元素
find() 查找一个元素
get_allocator() 返回map的配置器
insert() 插入元素
key_comp() 返回比较元素key的函数
lower_bound() 返回键值>=给定元素的第一个位置
max_size() 返回可以容纳的最大元素个数
rbegin() 返回一个指向map尾部的逆向迭代器
rend() 返回一个指向map头部的逆向迭代器
size() 返回map中元素的个数
swap() 交换两个map
upper_bound() 返回键值>给定元素的第一个位置
value_comp() 返回比较元素value的函数

deque

deque(双端队列)是由一段一段的定量连续空间构成,可以向两端发展,因此不论在尾部或头部安插元素都十分迅速。 在中间部分安插元素则比较费时,因为必须移动其它元素。

deque定义在头文件deque中。

定义deque如下:

1
2
3
4
5
deque<int> a; // 定义一个int类型的双端队列a
deque<int> a(10); // 定义一个int类型的双端队列a,并设置初始大小为10
deque<int> a(10, 1); // 定义一个int类型的双端队列a,并设置初始大小为10且初始值都为1
deque<int> b(a); // 定义并用双端队列a初始化双端队列b
deque<int> b(a.begin(), a.begin()+3); // 将双端队列a中从第0个到第2个(共3个)作为双端队列b的初始值

容量函数:

1
2
3
4
5
容器大小:deq.size();
容器最大容量:deq.max_size();
更改容器大小:deq.resize();
容器判空:deq.empty();
减少容器大小到满足元素所占存储空间的大小:deq.shrink_to_fit();

添加函数:

1
2
3
4
5
头部添加元素:deq.push_front(const T& x);
末尾添加元素:deq.push_back(const T& x);
任意位置插入一个元素:deq.insert(iterator it, const T& x);
任意位置插入 n 个相同元素:deq.insert(iterator it, int n, const T& x);
插入另一个向量的 [forst,last] 间的数据:deq.insert(iterator it, iterator first, iterator last);

删除函数:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
头部删除元素:deq.pop_front();
末尾删除元素:deq.pop_back();
任意位置删除一个元素:deq.erase(iterator it);
删除 [first,last] 之间的元素:deq.erase(iterator first, iterator last);
清空所有元素:deq.clear();

删除键为bfff指向的元素
cmap.erase("bfff");

删除迭代器 key所指向的元素
map<string,int>::iterator key = cmap.find("mykey");
if(key!=cmap.end()) {
cmap.erase(key);
}

删除所有元素
cmap.erase(cmap.begin(),cmap.end())

访问函数:

1
2
3
4
下标访问:deq[1]; // 并不会检查是否越界
at 方法访问:deq.at(1); // 以上两者的区别就是 at 会检查是否越界,是则抛出 out of range 异常
访问第一个元素:deq.front();
访问最后一个元素:deq.back();

可以看到,deque 与 vector 的用法基本一致,除了以下几处不同:

  • deque 没有 capacity() 函数,而 vector 有;
  • deque 有 push_front() 和 pop_front() 函数,而 vector 没有;
  • deque 没有 data() 函数,而 vector 有。

priority_queue

priority_queue 容器适配器定义了一个元素有序排列的队列。默认队列头部的元素优先级最高。因为它是一个队列,所以只能访问第一个元素,这也意味着优先级最高的元素总是第一个被处理。

priority_queue 模板有 3 个参数,其中两个有默认的参数;第一个参数是存储对象的类型,第二个参数是存储元素的底层容器,第三个参数是函数对象,它定义了一个用来决定元素顺序的断言。因此模板类型是:

1
template <typename T, typename Container=std::vector<T>, typename Compare=std::less<T>> class priority_queue

priority_queue 实例默认有一个 vector 容器。函数对象类型 less<T> 是一个默认的排序断言(最大堆),最大的元素排在队列前面,出队的时候会出最大的元素,而入队的时候会重排,定义在头文件 function 中,决定了容器中最大的元素会排在队列前面。fonction 中定义了 greater<T>,用来作为模板的最后一个参数对元素排序,最小元素会排在队列前面,出队的时候最小的元素先出队,而入队的时候会重排。当然,如果指定模板的最巵一个参数,就必须提供另外的两个模板类型参数。

可以如下所示生成一个空的优先级队列:

1
2
3
std::priority_queue<std::string> words; 
std::priority_queue<int> lo; // 大顶堆
std::priority_queue<int, vector<int>, greater<int>> hi; // 小顶堆

可以用适当类型的对象初始化一个优先级队列:

1
2
std::string wrds[] { "one", "two", "three", "four"};
std::priority_queue<std::string> words { std::begin(wrds), std::end(wrds)}; // "two" "three" "one" "four"

对 priority_queue 进行操作有一些限制:

  • push(const T& obj):将obj的副本放到容器的适当位置,这通常会包含一个排序操作。
  • push(T&& obj):将obj放到容器的适当位置,这通常会包含一个排序操作。
  • emplace(T constructor a rgs...):通过调用传入参数的构造函数,在序列的适当位置构造一个T对象。为了维持优先顺序,通常需要一个排序操作。
  • top():返回优先级队列中第一个元素的引用。
  • pop():移除第一个元素。
  • size():返回队列中元素的个数。
  • empty():如果队列为空的话,返回true。
  • swap(priority_queue<T>& other):和参数的元素进行交换,所包含对象的类型必须相同。

set

在set中每个元素的值都唯一,而且系统能根据元素的值自动进行排序。应该注意的是set中数元素的值不能直接被改变。

添加元素

下面是一个使用 insert() 的示例:

1
2
3
4
5
6
7
std::set<string, std::greater<string>> words {"one", "two", "three"};
auto pr1 = words.insert("four");
auto pr2 = words.insert ("two") ;
auto iter3 = words.insert(pr.first, "seven");
words.insert ({ "five","six"}) ;
string wrds[] {"eight", "nine", "ten"};
words.insert(std::begin(wrds) , std::end(wrds));

插入单个元素会返回一个 pair<iterator,bool> 对象。插入单个元素和一个标识,会返回一个迭代器。插入一段元素或一个初始化列表就不会有返回值。当 insert() 的参数是初始化列表时,会用列表中的字符串创建 string 对象。下面是两个在 set 容器中创建元素的示例:

1
2
3
std::set<std::pair<string,string>> names;
auto pr = names.emplace("Lisa", "Carr");
auto iter = names.emplace_hint(pr.first, "Joe", "King");

这和 map 一样。成员函数 emplace() 会返回一个 pair 对象,而 emplace_hint() 只返回一个迭代器。前者的参数被直接传入元素的构造函数,用来创建元素。emplace_hint() 的第一个参数是一个迭代器,它指出了元素可能的插入位置,随后的参数会被传入元素的构造函数。

删除元素

成员函数 clear() 会删除 set 的所有元素。成员函数 erase() 会删除迭代器指定位置的元素或与对象匹配的元素。例如:

1
2
3
4
5
std::set<int> numbers {2, 4, 6, 8, 10, 12, 14};
auto iter = numbers.erase(++std::begin(numbers));
auto n = numbers.erase(12);
n = numbers.erase(13);
numbers.clear();

成员函数 erase() 可以删除一段元素:

1
2
3
4
std::set<int> numbers {2, 4, 6, 8, 10, 12, 14};
auto iter1 = std::begin(numbers); // iter1 points to 1st element
advance(iterl, 5); // Points to 6th element-12
auto iter = numbers.erase(++std:rbegin(numbers), iter1);// Remove 2nd to 5th inclusive. iter points to 12

如果 set 没有元素,成员函数 empty() 返回 true,成员函数 size() 返回它所包含的元素个数。如果担心无法在 set 中存储尽可能多的元素,可以调用成员函数 max_size() 来得到可存储的最大元素个数,这显然会是一个很大的值。

访问元素

set 的成员函数 find() 会返回一个和参数匹配的元素的迭代器。如果对象不在 set 中,会返回一个结束迭代器。例如:

1
2
3
4
std::set<string> words {"one", "two","three", "four","five"};
auto iter = words.find ("one") ; // iter points to "one"
iter = words.find(string{"two"}); // iter points to "two"
iter = words.find ("six"); // iter is std:: end (words)

调用成员函数 count() 可以返回指定键所对应的元素个数,返回值通常是 0 或 1,因为 set 容器中的元素是唯一的。set 容器模板定义了成员函数 equal_range()、lower_bound()、 upper_bound(),这和 multiset 容器在很大程度上是一致的。

迭代器

这个知识点不应该放在这里,但是我想把这篇文章当成是刷LeetCode时的查阅语法的地方,所以就姑且放在这里吧。

要访问顺序容器和关联容器中的元素,需要通过“迭代器(iterator)”进行。迭代器是一个变量,相当于容器和操纵容器的算法之间的中介。迭代器可以指向容器中的某个元素,通过迭代器就可以读写它指向的元素。从这一点上看,迭代器和指针类似。

迭代器按照定义方式分成以下四种。

1) 正向迭代器,定义方法:容器类名::iterator 迭代器名;

2) 常量正向迭代器,定义方法:容器类名::const_iterator 迭代器名;

3) 反向迭代器,定义方法:容器类名::reverse_iterator 迭代器名;

4) 常量反向迭代器,定义方法:容器类名::const_reverse_iterator 迭代器名;

通过迭代器可以读取它指向的元素,*迭代器名就表示迭代器指向的元素。通过非常量迭代器还能修改其指向的元素。

迭代器都可以进行++操作。反向迭代器和正向迭代器的区别在于:

  • 对正向迭代器进行++操作时,迭代器会指向容器中的后一个元素;
  • 而对反向迭代器进行++操作时,迭代器会指向容器中的前一个元素。

以下是不同容器对应的迭代器功能。

容器迭代器功能
vector随机访问
deque随机访问
list双向
set / multiset双向
map / multimap双向
stack不支持迭代器
queue不支持迭代器
priority_queue不支持迭代器

值得注意的有如下几点:

  1. 双向迭代器不支持用“<”进行比较
  2. 不支持随机访问的迭代器不能使用下标访问元素
  3. .end()得到的迭代器指向最后一个元素的下一个元素
  4. 判断一个容器是不是为空,若想用迭代器的用法,建议使用vec.begin() == vec.end(),不要使用*vec.begin() == NULL,会报错

例如,vector 的迭代器是随机迭代器,因此遍历 vector 容器有以下几种做法。下面的程序中,每个循环演示了一种做法。

【实例】遍历 vector 容器。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#include <iostream>
#include <vector>
using namespace std;
int main()
{
vector<int> v(100); //v被初始化成有100个元素
for(int i = 0;i < v.size() ; ++i) //size返回元素个数
cout << v[i]; //像普通数组一样使用vector容器
vector<int>::iterator i;
for(i = v.begin(); i != v.end (); ++i) //用 != 比较两个迭代器
cout << * i;
for(i = v.begin(); i < v.end ();++i) //用 < 比较两个迭代器
cout << * i;
i = v.begin();
while(i < v.end()) { //间隔一个输出
cout << * i;
i += 2; // 随机访问迭代器支持 "+= 整数" 的操作
}
}

list 容器的迭代器是双向迭代器。假设 v 和 i 的定义如下:

1
2
list<int> v;
list<int>::const_iterator i;

则以下代码是合法的:

1
2
for(i=v.begin(); i!=v.end(); ++i)
cout << *i;

以下代码则不合法:

1
2
for(i=v.begin(); i<v.end(); ++i)
cout << *i;

因为双向迭代器不支持用“<”进行比较。以下代码也不合法:

1
2
for(int i=0; i<v.size(); ++i)
cout << v[i];

因为 list 不支持随机访问迭代器的容器,也不支持用下标随机访问其元素。

在 C++中,数组也是容器。数组的迭代器就是指针,而且是随机访问迭代器。例如,对于数组 int a[10],int * 类型的指针就是其迭代器。则 a、a+1、a+2 都是 a 的迭代器。

string

string定义在头文件string中。

定义

string可以采用如下方式定义

1
2
3
4
string s1();  // si = ""
string s2("Hello"); // s2 = "Hello"
string s3(4, 'K'); // s3 = "KKKK"
string s4("12345", 1, 3); //s4 = "234",即 "12345" 的从下标 1 开始,长度为 3 的子串

以下将从字符串下标 n 开始、长度为 m 的字符串称为子串(n, m)

赋值

对 string 对象赋值

  1. 可以用 char* 类型的变量、常量,以及 char 类型的变量、常量对 string 对象进行赋值。例如:

    1
    2
    3
    string s1;
    s1 = "Hello"; // s1 = "Hello"
    s2 = 'K'; // s2 = "K”
  2. string 类还有 assign 成员函数,可以用来对 string 对象赋值。assign 成员函数返回对象自身的引用。例如:

    1
    2
    3
    4
    5
    string s1("12345"), s2;
    s3.assign(s1); // s3 = s1
    s2.assign(s1, 1, 2); // s2 = "23",即 s1 的子串(1, 2)
    s2.assign(4, 'K'); // s2 = "KKKK"
    s2.assign("abcde", 2, 3); // s2 = "cde",即 "abcde" 的子串(2, 3)

访问

1
2
3
string s = "hello";
char c = s[0]; // 按下标访问得到的char
char *cc = s.c_str(); // string转为char*

长度

求字符串的长度:length 成员函数返回字符串的长度。size 成员函数可以实现同样的功能。

拼接

string对象中字符串的连接:除了可以使用++=运算符对 string 对象执行字符串的连接操作外,string 类还有 append 成员函数,可以用来向字符串后面添加内容。append 成员函数返回对象自身的引用。如:

1
2
3
4
5
string s1("123"), s2("abc");
s1.append(s2); // s1 = "123abc"
s1.append(s2, 1, 2); // s1 = "123abcbc"
s1.append(3, 'K'); // s1 = "123abcbcKKK"
s1.append("ABCDE", 2, 3); // s1 = "123abcbcKKKCDE",添加 "ABCDE" 的子串(2, 3)

比较

除了可以用 <、<=、==、!=、>=、> 运算符比较 string 对象外,string 类还有 compare 成员函数,可用于比较字符串。compare 成员函数有以下返回值:

  • 小于 0 表示当前的字符串小;
  • 等于 0 表示两个字符串相等;
  • 大于 0 表示另一个字符串小。

如:

1
2
3
4
5
6
7
string s1("hello"), s2("hello, world");
int n = s1.compare(s2);
n = s1.compare(1, 2, s2, 0, 3); //比较s1的子串 (1,2) 和s2的子串 (0,3)
n = s1.compare(0, 2, s2); // 比较s1的子串 (0,2) 和 s2
n = s1.compare("Hello");
n = s1.compare(1, 2, "Hello"); //比较 s1 的子串(1,2)和"Hello”
n = s1.compare(1, 2, "Hello", 1, 2); //比较 s1 的子串(1,2)和 "Hello" 的子串(1,2)

交换

swap 成员函数可以交换两个 string 对象的内容。例如:

1
2
string s1("West”), s2("East");
s1.swap(s2); // s1 = "East",s2 = "West"

求子串

substr 成员函数可以用于求子串 (n, m),原型如下:

1
string substr(int n = 0, int m = string::npos) const;

调用时,如果省略 m 或 m 超过了字符串的长度,则求出来的子串就是从下标 n 开始一直到字符串结束的部分。例如:

1
2
3
string s1 = "this is ok";
string s2 = s1.substr(2, 4); // s2 = "is i"
s2 = s1.substr(2); // s2 = "is is ok"

插入字符串

insert 成员函数可以在 string 对象中插入另一个字符串,返回值为对象自身的引用。例如:

1
2
3
4
string s1("Limitless"), s2("00");
s1.insert(2, "123"); //在下标 2 处插入字符串"123",s1 = "Li123mitless"
s1.insert(3, s2); //在下标 2 处插入 s2 , s1 = "Li10023mitless"
s1.insert(3, 5, 'X'); //在下标 3 处插入 5 个 'X',s1 = "Li1XXXXX0023mitless"

扩容

1
2
3
string s("hello");
s.push_back('w');
s.resize(20);

格式互转

转int

C语言转换形式:

1
2
3
#include<cstring>
std::string str;
int i = atoi(str.c_str()); // 不做范围检查,超出上界,则输出上界,超出下界,则输出下界;

C++转换形式(C++11):

1
2
3
#include<cstring>
std::string str;
int i = std::stoi(str); //会做范围检查,超过范围会报错

参考

C++ vector用法(详解!!函数,实现)
C++ 标准模板库STL 队列 queue 使用方法与应用介绍(一)
C++ stack(STL stack)用法详解
C++ map用法
C++中的STL中map用法详解
C++迭代器(STL迭代器)iterator详解
C++ queue(STL queue)用法详解
[C++ STL] deque使用详解
vector删除元素之pop_back(),erase(),remove()
C++ priority_queue(STL priority_queue)用法详解
C++ string的用法和例子
C++ string类(C++字符串)完全攻略
C++ set添加、删除和访问(STL set添加、删除和访问)元素详解
c++优先队列(priority_queue)用法详解
c++中的atoi()和stoi()函数的用法和区别
C++ - string类型转换int类型
c++ map查找key
vector的几种初始化及赋值方式
c++ map删除元素的三种方式

------ 本文结束------
坚持原创技术分享,您的支持将鼓励我继续创作!

欢迎关注我的其它发布渠道