在C++里有写好的标准模板库,我们称为STL库。它实现了集合、映射表、栈、队列等数据结构和排序、查找等算法。我们可以很方便地调用标准库来进行各类操作。
动态数组 vector
引用库
有时候想开一个数组,但是却不知道应该开多大长度的数组合适,因为我们需要用到的数组可能会根据情况变动,这个时候我们就需要用到动态数组了。
C++中的动态数组写作 vector,它的实现被写在 vector 的头文件中,并在所有头文件之后加上一句 using namespace std
#include <iostream>
#include <vector>
using namespace std;
int main()
{
return 0;
}
构建一个动态数组
现在我们来构造一个动态数组,C++中直接构造一个vector 的语句为:
vector<T> vec;
这样我们定义了一个名为 vec 的储存 T 类型数据的动态数组。其中 T 是我们要储存的数据类型,可以是 int、float、double 或者其他自定义的数据类型等等。初始的时候 vec 是空的。
插入元素
C++中通过 push_back ( ) 方法在数组最后面插入一个新的元素
#include <iostream>
#include <vector>
using namespace std;
int main()
{
vector<int> vec;
vec.push_back(0);
vec.push_back(1);
vec.push_back(2);
for(int i = 0; i < vec.size(); i++)
{
cout << vec[i] << endl;
}
return 0;
}
获取长度、访问元素、修改元素
C++ 中通过 size ( ) 方法获取 vector 的长度,通过 [ ] 操作直接访问 vector 中的元素,这一点和数组是一样的。
#include <iostream>
#include <vector>
using namespace std;
int main()
{
vector<int> vec;
vec.push_back(0);
vec.push_back(1);
vec.push_back(2);
vec[1] = 2;
vec[2] = 3;
for(int i = 0; i < vec.size(); i++)
{
cout << vec[i] << endl;
}
return 0;
}
清空
C++需调用 clear( ) 来清空 vector 。
调用clear( ) 后,vector的size将变成0,但是它的容量 capacity 并未发生改变,clear只是删除数据,并未释放vector的内存。vector的clear( ) 不影响capacity,如果想要清空vector的元素,使用clear,如果想要释放vector的容量,可以使用swap。
vector<A>().swap(vec);
vec.swap(vector<A>());
C++ vector 方法总结
方法 | 功能 |
---|---|
push_back | 在末尾加入一个元素 |
pop_back | 在末尾弹出一个元素 |
size | 获取长度 |
clear | 清空 |
vector<int> v1(10); //v1有10个元素,每个都初始化为0
vector<int> v2{10}; //v2有1个元素,该元素值为10
vector<int> v3(10, 1); //v3有10个元素,每个都初始化为1
vector<string> v4(10); //v4有10个默认初始化的元素
vector<string> v5{"xiebs"}; //v5有1个元素,该元素值为"xiebs"
vector<string> v6(10, "xiebs"); //v6有10个值为"xiebs"的元素
集合 set
集合是数学中的一个基本概念,通俗地讲,集合是由一些不重复的数据组成的。比如 { 1 , 2 , 3 } 就是一个有1,2,3的集合。C++的标准库中的集合支持高效的插入、删除和查询操作,这三个操作的时间复杂度都是 O(lgn),其中n是当前集合中元素的个数。如果用数组,虽然插入的时间复杂度是 O(1),但是删除合查询都是 O(n),此时效率太低。在C++中我们常用的集合是set
引用库
C++中的集合实现被写在 set 的头文件中,并在所有头文件之后加上一句 using namespace std
set<T> s;
这样我们定义了一个名为s的、储存T类型数据的集合,其中T是集合要储存的数据类型。初始的时候s是空集合
插入元素
C++中用 insert( ) 方法向集合中插入一个新的元素。
注意:如果集合中已经存在了某个元素,再次插入不会产生任何效果,集合中是不会出现重复元素的。
#include <set>
#include <string>
using namespace std;
int main()
{
set<string> country;
country.insert("China"); // {"China"}
country.insert("America"); // {"China", "America"}
country.insert("France"); // {"China", "America", "France"}
return 0;
}
删除元素
C++中通过 erase( ) 方法删除集合中的一个元素,如果集合中不存在这个元素,不进行任何操作。
#include <set>
#include <string>
using namespace std;
int main()
{
set<string> country; // {}
country.insert("China"); // {"China"}
country.insert("America"); // {"China", "America"}
country.insert("France"); // {"China", "America", "France"}
country.erase("America"); // {"China", "France"}
country.erase("England"); // {"China", "France"}
return 0;
}
查找元素
C++中如果你想知道某个元素是否在集合中出现,你可以直接用 count( ) 方法。如果集合中存在我们要查找的元素,返回 1 ,否则返回 0 。
#include <set>
#include <string>
#include <stdio.h>
using namespace std;
int main()
{
set<string> country;
country.insert("China"); // {"China"}
country.insert("America"); // {"China", "America"}
country.insert("France"); // {"China", "America", "France"}
if (country.count("China"))
{
printf("China belong to country");
}
return 0;
}
遍历元素
C++ 通过迭代器可以访问集合中的每个元素,迭代器就好比只想集合中的元素的指针。如果你不了解迭代器,你只需要先记住。
#include <set>
#include <string>
#include <iostream>
using namespace std;
int main()
{
set<string> country; // {}
country.insert("China"); // {"China"}
country.insert("America"); // {"China", "America"}
country.insert("France"); // {"China", "America", "France"}
for (set<string>::iterator it = country.begin(); it != country.end(); ++it)
{
cout << (*it) << endl;
}
return 0;
}
注意:在C++中遍历set是从小到大进行的。
清空
C++中只需要调用 clear( ) 方法就可以清空 set
方法 | 功能 |
---|---|
insert | 插入一个元素 |
erase | 删除一个元素 |
count | 判断元素是否在set中 |
size | 获取元素的个数 |
clear | 清空 |
映射 map
映射是指两个集合之间的元素的相互对应关系。通俗地说,就是一个元素对应另外一个元素。比如一个姓名的集合 {“Tom”, “Jone”, “Marry”},班级集合{1, 2}。姓名与班级之间可以有如下的映射关系:
class(“Tom”) = 1 , class(“Jone”) = 2 , class(“Marry”) = 1
我们称其中的姓名集合为 关键字集合(key) , 班级集合为 值集合(value) 。
在 C++ 中我们常用的映射是 map
引用库
C++中的map实现被写在 map 的头文件中,并在所有头文件之后加上一句 using namespac std
#include <map>
using namespace std;
构造一个映射
现在我们来构造一个映射
在C++中,我们构造一个 map 的语句为:
map<T1,T2> m;
这样我们定义了一个名为 m 的从 T1 类型到 T2 类型的映射。初始的时候 m 是空映射。
插入映射
在 C++ 中通过 insert( ) 方法向集合中插入一个新的映射,参数是一个 pair 类型的结构。这里需要用到另外一个 STL 模板 —— 元组(pair)。
pair<int,char>(1,'a');
定义了一个整数 1 和字符 a 的 pair。我们向映射中加入了新映射对的时候就是通过加入 pair 来实现的。如果插入的 key 之前已经有了 value,不会用插入的新的 value 替代原来的 value,也就是此次插入是无效的。
#include <map>
#include <string>
using namespace std;
int main()
{
map<string, int> dict; // {}
dict.insert(pair<string, int>("Tom", 1)); // {"Tom"->1}
dict.insert(pair<string, int>("Jone", 2)); // {"Tom"->1, "Jone"->2}
dict.insert(pair<string, int>("Mary", 1)); // {"Tom"->1, "Jone"->2, "Mary"->1}
dict.insert(pair<string, int>("Tom", 2)); // {"Tom"->1, "Jone"->2, "Mary"->1}
return 0;
}
访问映射
在 C++ 中访问映射合数组一样,直接用 [] 就能访问。比如 dict[“Tom”] 就可以获取 “Tom” 的班级了。而这里有一个比较神奇的地方,如果没有对 “Tom” 做过映射的话,此时你访问 dict[“Tom”] ,系统将会自动为 “Tom” 生成一个映射,其 value 为对应类型的默认值。并且我们可以之后再给映射赋予新的值,比如 dict[“Tom”] = 3 ,这样为我们提供了另一种方便的插入手段。当然有些时候,我们不希望系统自动为我们生成映射,这时候我们需要检测 “Tom” 是否已经有映射了,如果已经有映射再继续访问。这时候就需要用 count( ) 函数进行判断。
#include <map>
#include <string>
#include <stdio.h>
using namespace std;
int main()
{
map<string, int> dict; // {}
dict["Tom"] = 1; // {"Tom"->1}
dict["Jone"] = 2; // {"Tom"->1, "Jone"->2}
dict["Mary"] = 1; // {"Tom"->1, "Jone"->2, "Mary"->1}
printf("Mary is in class %d\n", dict["Mary"]);
printf("Tom is in class %d\n", dict["Tom"]);
return 0;
}
查找关键字
在 C++ 中,如果你想知道某个关键字是否被映射过,你可以直接用 count( ) 方法。如果被映射过,返回 1 ,否则返回 0 。
#include <map>
#include <string>
#include <stdio.h>
using namespace std;
int main()
{
map<string, int> dict; // {}
dict["Tom"] = 1; // {"Tom"->1}
dict["Jone"] = 2; // {"Tom"->1, "Jone"->2}
dict["Mary"] = 1; // {"Tom"->1, "Jone"->2, "Mary"->1}
if (dict.count("Mary"))
{
printf("Mary is in class %d\n", dict["Mary"]);
}
else
{
printf("Mary has no class");
}
return 0;
}
遍历映射
在 C++ 中,通过迭代器可以访问映射中的每个映射,每个迭代器的 first 值对应 key,second 值对应 value。
#include <map>
#include <string>
#include <iostream>
using namespace std;
int main()
{
map<string, int> dict; // {}
dict["Tom"] = 1; // {"Tom"->1}
dict["Jone"] = 2; // {"Tom"->1, "Jone"->2}
dict["Mary"] = 1; // {"Tom"->1, "Jone"->2, "Mary"->1}
for (map<string, int>::iterator it = dict.begin(); it != dict.end(); ++it)
{
cout << it->first << " is in class " << it->second << endl;
}
return 0;
}
清空
C++ 中只需要调用 Clear( ) 即可清空 map。
C++中map常用方法总结
方法 | 功能 |
---|---|
insert | 插入一个元素 |
count | 判断关键字 |
erase | 删除关键字 |
size | 获取映射对个数 |
clear | 清空 |
栈 stack
实现一种 先进后出 的数据结构,是一个模板类。头文件 #include <stack>
用法 (以 int 型为例):
stack <int> s; //定义一个int型栈
s.empty(); //返回栈是否为空
s.size(); //返回当前栈中元素的个数
s.push(); //在栈顶上堆进一个元素
s.pop(); //删除掉栈顶上的元素
s.top(); //返回栈顶的元素,并不会删除
代码示例:
#include <iostream>
#include <stack>
using namespace std;
int main()
{
stack<int> s;
cout<<"stack empty? "<<s.empty()<<endl;
for(int i=0;i<5;i++)
{
s.push(i); //入栈
}
cout<<"stack empty? "<<s.empty()<<endl;
cout<<"stack size: "<<s.size()<<endl;
cout<<endl;
for(int i=0;i<5;i++)
{
cout<<"stack top: "<<s.top()<<endl;
s.pop(); //出栈
}
return 0;
}
队列 Queue
实现一种 先进先出 的数据结构,是一个模板类。头文件 #include <queue>
用法 ( 以 int 型为例 ):
queue<int> Q; //定义一个int型队列
Q.empty(); //返回队列是否为空
Q.size(); //返回当前队列长度
Q.front(); //返回当前队列的第一个元素
Q.back(); //返回当前队列的最后一个元素
Q.push(); //在队列后面插入一个元素, 比如插入数字5: Q.push(5)
Q.pop(); //从当前队列里,移出第一个元素
代码示例:
#include <iostream>
#include <queue>
using namespace std;
int main()
{
queue<int> Q;
cout<<"queue empty? "<<Q.empty()<<endl;
for(int i=0;i<5;i++)
{
Q.push(i); //进队列
}
cout<<"queue empty? "<<Q.empty()<<endl;
cout<<"queue size: "<<Q.size()<<endl;
cout<<endl;
for(int i=0;i<5;i++)
{
cout<<"queue front: "<<Q.front()<<endl;
Q.pop(); //出队列
}
return 0;
}
双向链表 list
头文件:#include <list>
1、构造函数
list<int> a{1,2,3}
list<int> a(n) //链表中有n个元素,每个元素值都是0
list<int> a(n, m) //链表中有n个元素,每个元素值都是m
list<int> a(first, last) //声明一个链表,其元素的初始值来源于由区间所指定的序列中的元素,first和last是迭代器
2、begin() 和 end()
通过调用 list 容器的成员函数 begin() 得到一个指向容器起始位置的 iterator,可以调用 list 容器的 end() 数来得到 list 末端下一位置
3、push_back() 和 push_front()
使用 list 的成员函数 push_back 和 push_front 插入一个元素到 list 中。其中 push_back() 是从list 的末端插入,而 push_front() 是从 list 的头部插入
4、empty()
判断 list 是否为空
5、resize()
调用 resize(n) 将 list 的长度改为只容纳 n 个元素,超出的元素将被删除。如果 n 比 list 原来的长度长,那么默认超出的部分元素置为0。也可以用 resize(n, m) 的方式将超出的部分赋值为m
list<int>b{1, 2, 3, 4};
b.resize(2); //list中输出元素:1,2
list<int>b{1, 2, 3, 4};
b.resize(6); //list中输出元素:1,2,3,4,0,0
list<int>b{1, 2, 3, 4};
b.resize(6,9); //list中输出元素:1,2,3,4,9,9
6、clear()
清空 list 中的所有元素
7、front() 和 back()
通过 front() 可以获得 list 容器中的头部元素,通过 back() 可以获得 list 容器的最后一个元素。注意:当 list 元素为空时,这时候调用 front() 和 back() 不会报错。因此在编写程序时,最好先调用 empty() 函数判断 list 是否为空,再调用 front() 和 back() 函数。
8、pop_back() 和 pop_front()
使用 pop_back() 可以删掉尾部第一个元素,pop_front() 可以删掉头部第一个元素。注意:list必须不为空,如果当 list 为空的时候调用 pop_back() 和 pop_front() 会使程序崩掉。
9、assign()
有两种使用情况:
— a.assign(n, val):将a中的所有元素替换成n个val元素
list<int> b{1,2,3,4,5};
b.assign(5,10); //b中的元素变为10, 10, 10, 10, 10
— a.assign(b.begin(), b.end())
list<int> a{6,7,8,9};
list<int> b{1,2,3,4,5};
b.assign(a.begin(),a.end()); //b中的元素变为6,7,8,9
10、swap()
交换两个链表。a.swap(b)和swap(a, b)都可以完成a链表和b链表的交换。
list<int>a{6,7,8,9};
list<int>b{1,2,3,4,5};
swap(a, b); //或a.swap(b),a中元素变为1,2,3,4,5 b中元素变为6,7,8,9
11、reverse()
可以实现 list 的逆置
list<int> b{1,2,3,4,5};
reverse(b.begin(),b.end()); //b中元素变为5,4,3,2,1
12、merge()
a.merge(b) 调用结束后b变为空,a中元素包含原来a和b的元素。
list<int>a{6,7,8,9};
list<int>b{2, 1, 3, 6, 5};
a.merge(b,greater<int>()); //a中元素变为:6,7,8,9,2,1,3,6,5
list<int>a{6,7,8,9};
list<int>b{2, 1, 3, 6, 5};
a.merge(b); //a中元素变为:2,1,3,6,5,6,7,8,9
13、insert()
在指定位置插入一个或多个元素
a.insert(a.begin(),100); //在a的开始位置(即头部)插入100
a.insert(a.begin(),2, 100); //在a的开始位置插入2个100
a.insert(a.begin(),b.begin(), b.end()); //在a的开始位置插入b从开始到结束的所有位置的元素
14、erase()
删除一个元素或一个区域的元素
a.erase(a.begin()); //将a的第一个元素删除
a.erase(a.begin(),a.end()); //将a的从begin()到end()之间的元素删除。
15、remove()函数
从list中删除元素
list<int>a{6,7,8,9,7,10};
a.remove(7); //删除了a中所有值为7的元素,此时a中元素为6,8,9,10