C++中的容器,顾名思义就是用来存放数据的,我们知道常用的数据结构有:栈、队列、数组、链表、集合、哈希等等。在这里头,又分为序列式容器以及关联式容器:
- 序列式容器:序列中每个元素都有它固定的位置,强调值与值之间的排序
- 关联式容器:类似二叉树,各个元素之间没有严格的物理上排序
vector
vector是类似数组的数据结构,不同的是数组是固定大小,而vector可以动态扩增。我们可以通过创建vector对象,并指定其数据类型来存放数据。此外,每一个容器都有迭代器。通过迭代器可以遍历容器中的各个元素,使用 vector::iterator 获取vector这种容器的迭代器类型,然后v.begin(),v.end()分别代表容器的第一个位置以及末尾的位置。
int main() {
vector<int> v;
v.push_back(10);
v.push_back(20);
v.push_back(30);
v.push_back(40);
vector<int>::iterator pBegin = v.begin();
vector<int>::iterator pEnd = v.end();
while (pBegin != pEnd) {
cout << *pBegin << endl;
pBegin++;
}
//第二种遍历方式
for (vector<int>::iterator it = v.begin(); it != v.end(); it++) {
cout << *it << endl;
}
cout << endl;
system("pause");
return 0;
}
我们也可以通过其他方式来给vector赋值
int main() {
vector<int> v1; //无参构造
for (int i = 0; i <5; i++){
v1.push_back(i);
}
vector<int> v2 = v1; // 拷贝构造
vector<int> v3; // 使用assign赋值
v3.assign(v1.begin(), v1.end());
vector<int> v4; //赋予十个元素,每个元素的值都为100
v4.assign(10, 100);
vector<int> v5(10, 100);//赋予十个元素,每个元素的值都为100
system("pause");
return 0;
}
如果我们想获取vector的大小以及容量可以通过以下几个函数:
vector<int> v1(10,0);
cout << "v1是否为空" << v1.empty() << endl;
cout << "v1的容量 = " << v1.capacity() << endl;
cout << "v1的大小 = " << v1.size() << endl;
这里的容量与大小的区别在于capacity指的是容器可以容纳最大数量,而size指的是目前容器有多少个元素。
如果我们想要对vector进行增加、删除以及查找数据的操作,容器中也是有提供指定的函数来支持。
int main() {
vector<int> v1;
//尾插
v1.push_back(10);
v1.push_back(20);
v1.push_back(30);
v1.push_back(40);
v1.push_back(50);
//尾删
v1.pop_back();
//插入
v1.insert(v1.begin(), 100);
v1.insert(v1.begin(), 2, 1000);
cout << "v1的第一个元素为: " << v1.front() << endl;
cout << "v1的最后一个元素为: " << v1.back() << endl;
//不通过迭代器的查询:
for (int i = 0; i < v1.size(); i++){
cout << v1.ai(i) << endl;
}
for (int i = 0; i < v1.size(); i++){
cout << v1[i] << endl;
}
//删除
v1.erase(v1.begin());
//清空
v1.erase(v1.begin(), v1.end());
v1.clear();
system("pause");
return 0;
}
erase只是删除容器中的元素,但是不会清除内存,而clear则是清除容器所在内存中的内容。
deque
deque提供双向插入、删除,也就是双端数组。vector对于头部的插入与删除效率过低,而deque速度比vector快不少。除此之外,deque的构造以及其他操作和vector基本差不多。在deque中,也可以通过empty()、size()、capacity()、front()、back()等查看相关属性。
int main() {
deque<int> d1; //无参构造函数
for (int i = 0; i < 10; i++)
{
d1.push_back(i);
}
deque<int> d2(d1.begin(),d1.end());
deque<int>d3(10,100);
deque<int>d4 = d3;
deque<int>d5;
d5 = d1;
deque<int>d6;
d6.assign(d1.begin(), d1.end());
deque<int>d7;
d7.assign(10, 100);
system("pause");
return 0;
}
因为deque支持双向操作,因此除却push_back()外,还有push_front()来在容器头部插入数据。
int main() {
deque<int> d;
//尾插
d.push_back(10);
d.push_back(20);
//头插
d.push_front(100);
d.push_front(200);
//尾删
d.pop_back();
//头删
d.pop_front();
d.insert(d.begin(), 1000);
d.insert(d.begin(), 2,10000);
deque<int>d2;
d2.push_back(1);
d2.push_back(2);
d2.push_back(3);
d.insert(d.begin(), 1000);
d.insert(d.begin(), 2, 10000);
d.insert(d.begin(),d2.begin(),d2.end());
system("pause");
return 0;
}
栈与队列
学过数据结构的朋友都知道栈是一种先进后出的数据结构;而队列则是先进先出的数据结构。
由于栈只有一个出入口,所以往栈中增添、删除元素的操作叫做入栈、出栈,也叫压栈、弹栈。
int main() {
stack<int> s;
s.push(10);
s.push(20);
s.push(30);
cout << "栈的大小为:" << s.size() << endl;
//栈不提供迭代器,也不支持随机访问
while (!s.empty()) {
//输出栈顶元素
cout << "栈顶元素为: " << s.top() << endl;
//弹出栈顶元素
s.pop();
}
system("pause");
return 0;
}
而队列有两个出口,这个数据结构允许从一端新增元素,从另一端删除元素,也叫作入队、出队。
int main() {
queue<int> q;
q.push(10);
q.push(20);
q.push(30);
cout << "队列大小为:" << q.size() << endl;
//和栈一样,队列不提供迭代器,更不支持随机访问
while (!q.empty()) {
//输出队头、队尾元素
cout << "队头元素" << q.front() << endl;
cout << "队尾元素" << q.back() << endl;
cout << endl;
//弹出队头元素
q.pop();
}
system("pause");
return 0;
}
至于栈与队列的其他操作,和上述vector容器操作没有什么差异。
链表
和数组在物理存储单元上连续存储不一样,链表的连续性不是体现在物理结构上,而是逻辑结构,它是通过链表中的指针链接实现连续性的。我们都知道,链表是由一个又一个的结点组成,其中一个是数据域,一个是指针域。在STL中,链表是一个双向循环链表。
链表优点在于采用动态存储分配,不会造成内存浪费和溢出,以及执行插入和删除时不需要移动大量元素,但是缺点也是显而易见的:遍历的时候效率过低。
int main() {
list<int>L1;
L1.push_back(10);
L1.push_back(20);
L1.push_back(30);
L1.push_back(40);
list<int>L2(L1.begin(),L1.end());
list<int>L3(L2);
list<int>L4(10, 1000);
list<int>L5;
L5 = L1;
list<int>L6;
L6.assign(L1.begin(), L1.end());
list<int>L7;
L7.assign(10, 100);
system("pause");
return 0;
}
使用list容器的操作和vector几乎一样:
int main() {
list<int> L;
//尾插
L.push_back(10);
L.push_back(20);
L.push_back(30);
//头插
L.push_front(100);
L.push_front(200);
L.push_front(300);
//查看是否为空以及大小
if (L.empty())
{
cout << "L为空" << endl;
}
else
{
cout << "L不为空" << endl;
cout << "第一个元素为: " << L.front() << endl;
cout << "最后一个元素为: " << L.back() << endl;
cout << "L的大小为: " << L.size() << endl;
}
//尾删
L.pop_back();
//头删
L.pop_front();
//插入
list<int>::iterator it = L.begin();
L.insert(++it, 1000);
//删除
it = L.begin();
L.erase(++it);
L.push_back(10000);
L.push_back(10000);
L.push_back(10000);
//删除容器中所有与elem值匹配的元素
L.remove(10000);
//清空
L.clear();
system("pause");
return 0;
}
但是链表不支持 L.at(0) 、L[0]方式访问,以及链表的迭代器是双向迭代器,所以不支持随机访问。
pair与map
pair和map都是键值对映射的关系。pair是一对数据组成的对组,map中所有元素都是pair,其中pair里的第一个元素为key,起到索引的作用,第二个元素为value,保存key的值,所有的元素会根据键值的大小进行排序。
int main() {
//两种方式创建pair
pair<string, int> p1(string("Tom"), 20);
cout << "姓名: " << p1.first << " 年龄: " << p1.second << endl;
pair<string, int> p2 = make_pair("Jerry", 10);
cout << "姓名: " << p2.first << " 年龄: " << p2.second << endl;
map<int, int> m1; //几种插入方法
m.insert(pair<int, int>(1, 10));
m.insert(pair<int, int>(2, 20));
m.insert(make_pair(3, 30));
m[4] = 40;
if (m.empty())
{
cout << "m为空" << endl;
}
else
{
cout << "m不为空" << endl;
cout << "m的大小为: " << m.size() << endl;
}
for (map<int, int>::iterator it = m.begin(); it != m.end(); it++)
{
cout << "key = " << it->first << " value = " << it->second << endl;
}
cout << endl;
//清空
m.erase(m.begin(),m.end());
m.clear();
system("pause");
return 0;
}
集合
set容器也是会自动排序,默认从小到大。集合分为set和multiset,set不可以插入重复数据,而multiset可以。set也是可以通过empty、size来查看大小。
int main() {
set<int> s1;
s1.insert(10);
s1.insert(30);
s1.insert(20);
s1.insert(40);
//插入重复数据不会报错,但是也不会生效。
s1.insert(20);
//清空
s1.erase(s1.begin());
s1.erase(30);
s1.clear();
multiset<int> ms;
ms.insert(10);
ms.insert(10);
for (multiset<int>::iterator it = ms.begin(); it != ms.end(); it++) {
cout << *it << " ";
}
cout << endl;
system("pause");
return 0;
}