容器
1.容器的定义
生活中:
容器是用来存放物品,水桶是水的容器,油桶是油的容器。
C++中:存放不同的数据结构类型指定的容器。
STL是C/C++开发中一个非常重要的模板,而其中定义的各种容器也是非常方便我们大家使用。
STL中的常用容器包括:
顺序性容器(vector、deque、list)、
关联容器(map、set)、
容器适配器(queue、stack)。
2.STL(Standard Template Library)的六大组件:
容器(containers)、迭代器(iterators)、空间配置器(allocator)、配接器(adapters)、算法(algorithms)、仿函数(functors)六个部分。
其交互关系:
容器通过空间配置器取得数据存储空间,空间配置器通过迭代器存取容器的内容,仿函数可以协助空间配置器完成不同的策略变化,配接器可以修饰或套接仿函数。
3.迭代器
迭代器是STL的一个重要组成部分。每个容器都有自己的迭代器。
可以迭代器看作一个容器使用的特殊指针,可以用来存取容器内存储的数据。
迭代器的定义格式:
<容器名><数据类型>iterator 迭代器变量名;
迭代器有5种:
输入(input iterators),
输出(output iterators),
向前(forword iterators),
双向(bidirectional iterators)和
随机存取(random access iterators)
Input Iterator:从容器中读取元素。输入迭代器只能一次读入一个元素向前移动,每次只能读一次。
Output Iterator:向容器中写入元素。输出迭代器只能一次一个元素向前移动。每次只能写一个数据
Forward Iterator:不仅有input ,output功能,还能具有多次读写的功能;
Bidirectional Iterator:以前向迭代器为基础,添加了后退功能。
Random Access Iterator:组合双向迭代器的功能与直接访问容器中任何元素的功能,即可向前向后跳过任意个元素。向前向后随意移动。
这五类迭代器的从属关系,如下图所示,其中箭头A→B表示,A是B的强化类型,这也说明了如果一个算法要求B,那么A也可以应用于其中。
input output
\ /
forward
|
bidirectional
|
random access
4.顺序容器
中的容器类包括“顺序存储结构”和“关联存储结构”:
前者包括vector,list,deque等;
后者包括set,map,multiset,multimap等。
若需要存储的元素数在编译器间就可以确定,可以使用数组来存储,否则,就需要用到容器类了。
Vector类和deque类是以数组为基础的;list类是以双向链表为基础的
1)向量(vector类)提供了l连续内存地址的数据结构,可以通过[ ]直接访问任何元素。
与数组不同,vector内存用尽时,将自动分配更大的连续内存区,将原来的元素复制到新的内存区,并释放旧的内存区。
可以实现队列,堆栈,列表和其他的负责结构。Vector支持随机访问数据,通常称为vector元素的指针。
向量定义在头文件#include <vector>
2)列表(list):是由双向链表组成,有关链表的操作都适合。可前可后,不能随机访问、
<list>
3)双向队列(deque)由双端队列组成,允许在队列的两端操作。支持随机访问和[]访问。
5.顺序容器
顺序容器:将单一类型元素聚集起来成为容器,然后根据位置来存储和访问这些元素,这就是顺序容器。
顺序容器的元素排列次序与元素值无关,而是由元素添加到容器里的次序决定。
Vector:从后面快速的插入与删除,直接访问任何元素
Deque:从前面或后面快速的插入与删除,直接访问任何元素
List:双链表,从任何地方快速插入与删除
6.关联容器
Set:快速查找,不允许重复值
Multiset :快速查找,允许重复值
Map:
一对多映射,基于关键字快速查找,不允许重复值
Multimap:
一对多映射,基于关键字快速查找,允许重复值
7.容器适配器
Stack:后进先出
Queue:先进先出
priority_queue:最高优先级元素总是第一个出列
8.所有标准库共有的函数
默认构造函数:提供容器默认初始化的构造函数。
复制构造函数:将容器初始化为现有同类容器副本的构造函数
析构函数:不再需要容器时进行内存整理的析构函数
Empty:容器中没有元素时返回true,否则返回false
max_size:返回容器中最大元素个数
Size:返回容器中当前元素个数
Operator= :将一个容器赋给另一个容器
operator< :如果第一个容器小于第二个容器,返回true,否则返回false,
operator<=: 如果第一个容器小于或等于第二个容器,返回true,否则返回false
operator> :如果第一个容器大于第二个容器,返回true,否则返回false
operator>= :如果第一个容器大于或等于第二个容器,返回true,否则返回false
operator== :如果第一个容器等于第二个容器,返回true,否则返回false
operator!= :如果第一个容器不等于第二个容器,返回true,否则返回false
Swap :交换两个容器的元素
其中:perator>,operator>=,operator<,operator<=,operator==,operator!=均不适用于priority_queue
9.顺序容器和关联容器共有函数
Begin :该函数两个版本返回iterator或const_iterator,引用容器第一个元素
End :该函数两个版本返回iterator或const_iterator,引用容器最后一个元素后面一位
Rbegin :该函数两个版本返回reverse_iterator或const_reverse_iterator,引用容器最后一个元素
Rend:该函数两个版本返回reverse_iterator或const_reverse_iterator,引用容器第一个元素前面一位
Erase :从容器中清除一个或几个元素
Clear :清除容器中所有元素
10.顺序容器和关联容器中常用的typedef
value_type:容器中存放元素的类型
Reference:容器中存放元素类型的引用
const_reference:容器中存放元素类型的常量引用,这种引用只能读取容器中的元素和进行const操作
Pointer:容器中存放元素类型的指针
Iterator:指向容器中存放元素类型的迭代器
const_iterator:指向容器中存放元素类型的常量迭代器,只能读取容器中的元素
reverse_iterator:指向容器中存放元素类型的逆向迭代器,这种迭代器在容器中逆向迭代
const_reverse_iterator:指向容器中存放元素类型的逆向迭代器,只能读取容器中的元素
difference_type:用相同容器的两个迭代器相减结果的类型(list和关联容器没有定义operator-)
size_type:用于计算容器中项目数和检索顺序容器的类型(不能对list检索)
11.vector
1)vector向量相当于一个数组
在内存中分配一块连续的内存空间进行存储。支持不指定vector大小的存储。
STL内部实现时,首先分配一个非常大的内存空间预备进行存储,即capacity()函数返回的大小,当超过此分配的空间时再整体重新放分配一块内存存储,这给人以vector可以不指定vector即一个连续内存的大小的感觉。通常此默认的内存分配能完成大部分情况下的存储。
用法:
文件包含:
#include<vector>
using namespace std;
变量声明:
声明一个int向量以替代一维的数组:
vector <int> a;
等于声明了一个int数组a[],大小没有指定,可以动态的向里面添加删除)。
(1)头文件#include<vector>.
(2)创建vector对象,vector<int> vec;
(3)尾部插入数字: vec.push_back(a);
(4)使用下标访问元素,cout<<vec[0]<<endl;记住下标是从0开始的。
(5)使用迭代器访问元素.
vector<int>::iterator it;
for(it=vec.begin(); it!=vec.end(); it++)
cout<<*it<<endl;
(6)插入元素:vec.insert(vec.begin()+i,a);
在第i+1个元素前面插入a;
(7)删除元素:
vec.erase(vec.begin()+2); 删除 第3个元素
vec.erase(vec.begin()+i,vec.end()+j); 删除区间[i,j-1];区间从0开始
(8)向量大小:vec.size();
(9)清空:vec.clear();
3. 初始化
vector // 创建一个空的vector。
vector c1(c2) // 复制一个vector
vector c(n) // 创建一个vector,含有n个数据,数据均已缺省构造产生
vector c(n, elem) // 创建一个含有n个elem拷贝的
4. 析构函数
c.~vector () // 销毁所有数据,释放内存
5. 成员函数
c.assign(beg,end) c.assign(n,elem)
将[beg; end)区间中的数据赋值给c。将n个elem的拷贝赋值给c。
c.at(idx) 传回索引idx所指的数据,如果idx越界,抛出out_of_range。
c.back() // 传回最后一个数据,不检查这个数据是否存在
c.begin() // 传回迭代器中的第一个数据地址。
c.capacity() // 返回容器中数据个数。
c.clear() // 移除容器中所有数据。
c.empty() // 判断容器是否为空。
c.end() // 指向迭代器中末端元素的下一个,指向一个不存在元素。
c.erase(pos) // 删除pos位置的数据,传回下一个数据的位置。
c.erase(beg,end) //删除[beg,end)区间的数据,传回下一个数据的位置。
c.front() // 传回第一个数据。
get_allocator // 使用构造函数返回一个拷贝。
c.insert(pos,elem) // 在pos位置插入一个elem拷贝,传回新数据位置。
c.insert(pos,n,elem) // 在pos位置插入n个elem数据。无返回值。
c.insert(pos,beg,end) // 在pos位置插入在[beg,end)区间的数据。无返回值。
c.max_size() // 返回容器中最大数据的数量。
c.pop_back() // 删除最后一个数据。
c.push_back(elem) // 在尾部加入一个数据。
c.rbegin() // 传回一个逆向队列的第一个数据。
c.rend() // 传回一个逆向队列的最后一个数据的下一个位置。
c.resize(num) // 重新指定队列的长度。
c.reserve() // 保留适当的容量。
c.size() // 返回容器中实际数据的个数。
c1.swap(c2)
swap(c1,c2) // 将c1和c2元素互换。同上操作。
operator[] // 返回容器中指定位置的一个引用。
1.push_back : 在数组的最后添加一个数据
2.pop_back: 去掉数组的最后一个数据
3.at : 得到编号位置的数据
4.begin 得到数组头的指针
5.end 得到数组的最后一个单元+1的指针
6front 得到数组头的引用
7.back 得到数组的最后一个单元的引用
8.max_size 得到vector最大可以是多大
9.capacity 当前vector分配的大小
10.size 当前使用数据的大小
11.resize 改变当前使用数据的大小,如果它比当前使用的大,者填充默认值
12.reserve 改变当前vecotr所分配空间的大小
13.erase 删除指针指向的数据项
14.clear 清空当前的vector
15.rbegin 将vector反转后的开始指针返回(其实就是原来的end-1)
16.rend 将vector反转构的结束指针返回(其实就是原来的begin-1)
17.empty 判断vector是否为空
18.swap 与另一个vector交换数据
6.详细的函数实现功能:其中vector<int> c.
c.clear() 移除容器中所有数据。
c.empty() 判断容器是否为空。
c.erase(pos) 删除pos位置的数据
c.erase(beg,end) 删除[beg,end)区间的数据
c.front() 传回第一个数据。
c.insert(pos,elem) 在pos位置插入一个elem拷贝
c.pop_back() 删除最后一个数据。
c.push_back(elem) 在尾部加入一个数据。
c.resize(num) 重新设置该容器的大小
c.size() 回容器中实际数据的个数。
c.begin() 返回指向容器第一个元素的迭代器
c.end() 返回指向容器最后一个元素的迭代器
:将[beg; end)区间中的数据赋值给c。
c.assign(n,elem):将n个elem的拷贝赋值给c。
c.at(idx):传回索引idx所指的数据,如果idx越界,抛出out_of_range。
c.back():传回最后一个数据,不检查这个数据是否存在。
c.front():传回地一个数据。
get_allocator:使用构造函数返回一个拷贝。
c.rbegin():传回一个逆向队列的第一个数据。
c.rend():传回一个逆向队列的最后一个数据的下一个位置。
c.~ vector <Elem>():销毁所有数据,释放内存。
c.back() // 传回最后一个数据,不检查这个数据是否存在。
c.begin() // 传回迭代器中的第一个数据地址。
c.capacity() // 返回容器中数据个数。
c.clear() // 移除容器中所有数据。
c.empty() // 判断容器是否为空。
c.end() // 指向迭代器中末端元素的下一个,指向一个不存在元素。
c.erase(pos) // 删除pos位置的数据,传回下一个数据的位置。
c.erase(beg,end) //删除[beg,end)区间的数据,传回下一个数据的位置。
c.front() // 传回第一个数据。
get_allocator // 使用构造函数返回一个拷贝。
c.insert(pos,elem) // 在pos位置插入一个elem拷贝,传回新数据位置。
c.insert(pos,n,elem) // 在pos位置插入n个elem数据。无返回值。
c.insert(pos,beg,end) // 在pos位置插入在[beg,end)区间的数据。无返回值。
c.max_size() // 返回容器中最大数据的数量。
c.pop_back() // 删除最后一个数据。
c.push_back(elem) // 在尾部加入一个数据。
c.rbegin() // 传回一个逆向队列的第一个数据。
c.rend() // 传回一个逆向队列的最后一个数据的下一个位置。
c.resize(num) // 重新指定队列的长度。
c.reserve() // 保留适当的容量。
c.size() // 返回容器中实际数据的个数。
c1.swap(c2)
swap(c1,c2) // 将c1和c2元素互换。同上操作。
operator[] // 返回容器中指定位置的一个引用。
优点:
不指定一块内存大小的数组的连续存储,即可以像数组一样操作,但可以对此数组进行动态操作。通常体现在push_back() pop_back()
随机访问方便,即支持[ ]操作符和vector.at()
节省空间。
缺点:
在内部进行插入删除操作效率低。
只能在vector的最后进行push和pop,不能在vector的头进行push和pop。
(3) 当动态添加的数据超过vector默认分配的大小时要进行整体的重新分配、拷贝与释放
List双向链表
list 双向链表
每一个结点都包括一个信息快Info、一个前驱指针Pre、一个后驱指针Post。可以不分配必须的内存大小方便的进行添加和删除操作。使用的是非连续的内存空间进行存储。
优点:
不使用连续内存完成动态操作。
在内部方便的进行插入和删除操作
可在两端进行push、pop
缺点:
不能进行内部的随机访问,即不支持[ ]操作符和vector.at()
相对于verctor占用内存多
Lists将元素按顺序储存在链表中. 与 向量(vectors)相比, 它允许快速的插入和删除,但是随机访问却比较慢.
assign() 给list赋值
back() 返回最后一个元素
begin() 返回指向第一个元素的迭代器
clear() 删除所有元素
empty() 如果list是空的则返回true
end() 返回末尾的迭代器
erase() 删除一个元素
front() 返回第一个元素
get_allocator() 返回list的配置器
insert() 插入一个元素到list中
max_size() 返回list能容纳的最大元素数量
merge() 合并两个list
pop_back() 删除最后一个元素
pop_front() 删除第一个元素
push_back() 在list的末尾添加一个元素
push_front() 在list的头部添加一个元素
rbegin() 返回指向第一个元素的逆向迭代器
remove() 从list删除元素
remove_if() 按指定条件删除元素
rend() 指向list末尾的逆向迭代器
resize() 改变list的大小
reverse() 把list的元素倒转
size() 返回list中的元素个数
sort() 给list排序
splice() 合并两个list
swap() 交换两个list
unique() 删除list中重复的元素
deque
双端队列 double-end queue
是在功能上合并了vector和list。
优点:
(1) 随机访问方便,即支持[ ]操作符和vector.at()
(2) 在内部方便的进行插入和删除操作
(3) 可在两端进行push、pop
缺点:(1) 占用内存多
使用区别:
1)如果你需要高效的随即存取,而不在乎插入和删除的效率,使用vector
2)如果你需要大量的插入和删除,而不关心随机存取,则应使用list
3)如果你需要随机存取,而且关心两端数据的插入和删除,则应使用deque
关联容器
关联容器与顺序容器的本质区别在于:
关联容器是通过键(key)存储和读取元素的,
而顺序容器则通过元素在容器中的位置顺序存储和访问元素。
关联容器支持通过键来高效地查找和读取元素,两个基本的关联容器是map和set。
map的元素是“键-值”对的二元组形式:
键用作元素在map中的索引,而值则表示所存储和读取的数据。
set仅包含一个键,并有效地支持关于某个键是否存在的查询。
set和map类型的对象所包含的元素都具有不同的键。
如果需要一个键对应多个实例,则需要使用multimap或multiset类型。这两种类型允许多个元素拥有相同的键。
map 关联数组:元素通过键来存储和读取
set 大小可变的集合,支持通过键实现的快速读取
multimap 支持同一个键多次出现的map类型
multiset 支持同一个键多次出现的set类型
Map映射
Map是STL的一个关联容器,它提供一对一(其中第一个可以称为关键字,每个关键字只能在map中出现一次,第二个可能称为该关键字的值)的数据处理能力,由于这个特性,它完成有可能在我们处理一对一数据的时候,在编程上提供快速通道。
Map由key和value组成,
给定一个唯一的key就能找到value
Map中是以pair<key,value>存储的
map提供大小可变的关联容器,基于关联键值高效检索元素值。当你处理键值对的数据是,都可以考虑使用map关联容器。
特点:
大小可变的关联容器,基于关联键值高效检索元素值。
可逆,因为它提供双向迭代器来访问其元素。
有序,因为它的元素根据指定的比较函数按键值排序。
唯一。 因为它的每个元素必须具有唯一键。
关联容器对,因为它的元素数据值与其键值不同。
模板类,因为它提供的功能是一般性的功能,与元素或键类型无关。 用于元素和键的数据类型作为类模板以及比较函数和分配器中的参数指定。
map最基本的构造函数;
map<string , int > mapstring;
map<int ,string > mapint;
map<sring, char> mapstring;
map< char ,string>mapchar;
map<char ,int> mapchar;
;
添加数据;
map<int ,string> maplive;
1.maplive.insert(pair<int,string>(102,"aclive"));
2.maplive.insert(map<int,string>::value_type(321,"hai"));
3, maplive[112]="April";
//map中最简单最常用的插入添加!
map中元素的查找:
find()函数返回一个迭代器指向键值为key的元素,如果没找到就返回指向map尾部的迭代器。
map<int ,string >::iterator l_it;;
l_it=maplive.find(112);
if(l_it==maplive.end())
cout<<"we do not find 112"<<endl;
else cout<<"wo find 112"<<endl;
map中元素的删除:
如果删除112;
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;
的基本操作函数:
C++ Maps是一种关联式容器,包含“关键字/值”对
begin() 返回指向map头部的迭代器
clear() 删除所有元素
count() 返回指定元素出现的次数
empty() 如果map为空则返回true
end() 返回指向map末尾的迭代器
equal_range() 返回特殊条目的迭代器对
erase() 删除一个元素
find() 查找一个元素
get_allocator() 返回map的配置器
insert() 插入元素
key_comp() 返回比较元素key的函数
返回键值>=给定元素的第一个位置
max_size() 返回可以容纳的最大元素个数
rbegin() 返回一个指向map尾部的逆向迭代器
rend() 返回一个指向map头部的逆向迭代器
size() 返回map中元素的个数
swap() 交换两个map
upper_bound() 返回键值>给定元素的第一个位置
value_comp() 返回比较元素value的函数
头文件:#include <map>
命名空间:using namespace std:map;
构造函数:map<key_type, value_type>map_show
表示键类型,value_type表示值类型,
如:map<string, int>map_show;表示以字符串为键,整形位置的关联map
map<int, int>map_int; // 类似数组
map<string, int>map_str; // 类似以字符串为下标的数组
下标访问“[]” 和at()方法两种方式,推荐第二种方法,它会进行边界检查。对于map<string, int>map_str;其访问方式为:
1. map_str[“firstel”];或者map_str.at(“firstel”); // 返回键值firstel对应的值
2. int size( ) const; // 返回映射中的元素数量。
3. bool empty( ) const; // 判断映射是否为空,true表示为空,false表示为非空
4. iterator find(const Key& key);
// 返回映射中具有与指定键等效的键对应元素位置的迭代器。
注意:count方法:map_str.count("first") // 返回 map 中其键与指定了参数的键匹配的元素数量。
如果 map 包含其排序键与参数键匹配的元素,则返回 1;如果 map 不包含带有匹配键的元素,则返回 0。
2.添加
对于map<string, int>map_str,有以下几种插入方式:
1. map_str.insert(make_pair("key", 23));
将键值对{"key", 23}插入到映射中
2. map_str.insert({"key", 23});
将键值对{"key", 23}插入到映射中
3.map_str.insert(map_str.begin()+2, make_pair("key", 23));
在第二个位置插入{"key", 23}键值对
3.删除
1. iterator erase(const_iterator Where);
移除Where迭代器位置的键值对
2. int erase(const key_type& key);
移除键值和key相等的位置的键值对
3.iterator erase( const_iterator First, const_iterator Last );
移除First和Last迭代器之间的键值对
4.void clear();
清除映射内的所有键值对元素
4.遍历
使用迭代器进行遍历,非常方便。
1.iterator begin( ); // 返回指向映射中第一个元素的迭代器
2.iterator end( ); // 返回指向映射中最后一个元素的迭代器
for (map<string, int>::iterator map_it = map_str.begin(); map_it != map_str.end(); map_it++)
{ cout << "key: " << map_it->first << "value: " << map_it->second;
// map_it->first表示键值对的第一个值,也就是键的值,map_it->second表示键值对的第二个值,也就是键对应的值
}
成员函数
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的函数
适配器
适配器和顺序容器结合,提供了堆栈,队列和优先队列等功能。有容器适配器,迭代适配器,函数对象适配器。
一、标准库顺序容器适配器的种类
标准库提供了三种顺序容器适配器:
queue(FIFO队列)、priority_queue(优先级队列)、stack(栈)
二、什么是容器适配器
”适配器是使一种事物的行为类似于另外一种事物行为的一种机制”,适配器对容器进行包装,使其表现出另外一种行为。 例如:
stack<int, vector<int> >实现了栈的功能,但其内部使用顺序容器vector<int>来存储数据。(相当于是vector<int>表现出了栈的行为)。
三、容器适配器
要使用适配器,需要加入一下头文件:
#include <stack> //stack
#include<queue> //queue、priority_queue
stack
建立容器的格式:
Template <class T, class Contain =deque <T> >
class stack;
T:元素类型, contain:要使用的容器类型,默认deque
stack(堆栈)是一个容器的改编,它实现了一个先进后出的数据结构(FILO)
stack 模板类的定义在<stack>头文件中。
stack 模板类需要两个模板参数,一个是元素类型,一个容器类型,但只有元素类型是必要
的,在不指定容器类型时,默认的容器类型为deque。
定义stack 对象的示例代码如下:
stack<int> s1;
stack<string> s2;
stack的基本操作有:
1.入栈:如s.push(x);
2.出栈:如 s.pop().注意:出栈操作只是删除栈顶的元素,并不返回该元素。
3.访问栈顶:如s.top();
4.判断栈空:如s.empty().当栈空时返回true。
5.访问栈中的元素个数,如s.size();
算法
用来查找一个或多个数据
1.find
在某个范围内查找数据,格式:
Template <class inputiterator ,class T>
Inputiterator.find(inputiterator first, inputiterator last,
const T &value)
说明:在容器V1的[first,last]范围内查找value;
返回迭代器类型,如果找到该数据,则指向该数据在容器中第一次出现的位置,否则指向结果序列的尾部。
2.Search
用于两个范围查找:
Template <class forwordterator1 , class forwordterator2 >
Forwordterator1.search(forwordterator1 first1, forwordterator1 last1, forwordterator2 first2, forwordterator2 last2 );
说明:在容器[fist1,last1]中查找[first2,last2]是否存在;
返回迭代器类型,如果找到该数据,则指向该数据在容器中第一次出现的位置,否则指向结果序列的尾部。
排序算法
用来对容器中数据进行排序,反转或者交换
1.sort
对容器中数据进行排序:
Template <class RandomAccessiterator>
Void sort (RandomAccessiterator first, RandomAccessiterator last);
对容器中[first,last]进行排序,默认升序排序
2.reverse
对容器内数据进行反转。
Template <class BidirectionalIterator>
Void reverse(BidirectionalIterator fisrt,
对 BidirectionalIterator last)
容器内数据进行反转
3.swap
交换两个数据的值:
Template <class T>
Void swap(T&a, T &b);
交换a和b的值;