STL是C++提供标准模块库的缩写,其中涉及多诸多常用的数据结构和算法,本文便是介绍map结构,一种和hash-table同样使用键值对的数据结构,但和哈希表常用数组实现不同,STL::map内核数据结构是红黑树,故而相比于hash-table的搜索速度是O(1), 而STL::map搜索时间复杂度是O(LogN)。但考虑到空间复杂度和动态扩展性能,以及要设计表现优异的hash函数的难度,故而对于存在较多数据量要管理的情况,并且管理过程较为动态,推荐使用STL::map可以在空间复杂度和时间复杂度取得较好的均衡。
STL::map的常用API列举如下:
函数名 | 功能 |
begin() | 返回指向该map结构的首项的迭代器iterator |
end() | 返回指向该map结构的末尾的迭代器iterator,是空值 |
rbegin() | 返回指向该map结构的尾部项目的逆向迭代器reverse_iterator |
rend() | 返回指向该map结构的头部的逆向迭代器reverse_iterator,是空值 |
equal_range() | 返回指向该map结构匹配特殊Key搜索的迭代器对,解释起来很难懂,看下面代码就简单了 |
erase() | 根据输入参数指定的Key值,擦除map中指定的数据项,存在三种重载方式,见代码 |
find() | 根据输入参数指定的Key值,返回map数据结构中匹配该Key值的数据项处的迭代器 |
count() | 判断输入参数指定Key值的键值对是否出现在map中,出现返回1,否则返回0 |
…… |
#include <map>
#include <string>
#include <iostream>
//#pragma warning (disable:4530)
using namespace std;
typedef struct studentGradeAndNameInfo
{
int grade;
string studentName;
bool operator < (const studentGradeAndNameInfo & A) const
{
if (grade < A.grade) return true;
if (grade == A.grade)
return studentName.compare(A.studentName) < 0;
return false;
}
}StudentInfo, *PStudentInfo;
typedef struct FoodAndPrice
{
int price;
string vegetables;
}FoodInfo;
class sort
{
public:
bool operator() (FoodInfo const & A, FoodInfo const & B) const
{
if (A.price < B.price)
return true;
if (A.price == B.price)
return A.vegetables.compare(B.vegetables) < 0;
return false;
}
};
int main(){
map<int, string> mapStudent;
//在map中插入新元素的操作过程是先根据key值,在Map中看能不能找到有此前的老元素,若是没有,则会新建一个新元素,
//并先按照缺省值初始化,所以如果第二项数据项是类这种初始化消耗较为明显的元素时,如果采用mapStudent[112] = "string"
//则会涉及到两次对象初始化,为了避免这种无效操作,可以采用如下的方式进行初始化,只需要一次。
mapStudent.insert(map<int, string>::value_type(1, "ss"));
mapStudent.insert(map<int, string>::value_type(2, "tt"));
mapStudent.insert(map<int, string>::value_type(3, "ll"));
//但需要注意的是上面这种用法,是只能用于插入新数据,如果要覆盖此前map中就有的元素,则只能采用mapStudent[112] = "string"这种方式
mapStudent.insert(map<int, string>::value_type(1, "stl"));
mapStudent[2] = "stl";
//所以这里就要介绍的是C++11采用了Lisp类似的函数多返回值机制,但是依旧没有Lisp的好使
//其实mapStudenet.inset()是存在多个返回值的,第一个返回值是map内部迭代器,第二个是本次操作成功与否标示符,两者封装在一起位pair类型
map<int, string>::iterator test;
pair<map<int,string>::iterator, bool> insert_return_pair;
//test = mapStudent.insert(map<int, string>::value_type(4, "test"));
//这种使用方式是不行的,Lisp的多返回值可以选择性抛弃,而这里的mapStudent.insert返回的是std::pair<>类型参数
insert_return_pair = mapStudent.insert(map<int, string>::value_type(4, "test"));
/*
test = insert_return_pair.first;
for (; test != mapStudent.begin(); test--)
cout<<test->first<<" && "<<test->second<<endl;
*/
if (insert_return_pair.second)
cout<<"成功插入新元素"<<endl;
else
cout<<"本次插入元素失败"<<endl;
/*map大小的获取方式*/
int map_size = mapStudent.size();
cout<<"当前map的size:"<<map_size<<endl<<endl<<endl;
/*遍历map的几种方式*/
cout<<"遍历map的第一种方式:使用前向迭代器iterator,其中关键函数有map.begin(),map.end()"<<endl;
map<int, string>::iterator iter; //定义迭代器
for (iter = mapStudent.begin(); iter != mapStudent.end(); iter++)
cout<<iter->first<<" &&& "<<iter->second<<endl;
cout<<"遍历map的第二种方式:使用反迭代器reverse_iterator,其中关键函数有map.rbegin(),map.rend()"<<endl;
map<int, string>::reverse_iterator reverse_iter;
for (reverse_iter = mapStudent.rbegin(); reverse_iter != mapStudent.rend(); reverse_iter++)
cout<<reverse_iter->first<<" *** "<<reverse_iter->second<<endl;
cout<<"遍历map最直接的第三种方式,用数据遍历,关键函数是map.size()"<<endl;
for (int i = 1; i < map_size+1; i++)
cout<<i<<" xxx "<<mapStudent[i]<<endl;
cout<<endl<<endl;
/*map指定数据的几种查找方式*/
cout<<"查找指定数据的第一种方式:使用count,该函数的输入参数为key,但该函数只返回指定key的记录是否出现,1为出现,0为否,返回信息有限"<<endl;
if ( mapStudent.count(2) )
cout<<"在map中存在test关键字,但具体位置和出现次数不知道"<<endl;
cout<<"查找指定数据的第二种方式:使用find,该函数输入参数为Key,该函数返回指定关键字出现的位置的迭代器即指针"<<endl;
iter = mapStudent.find( 3 );
if (iter != mapStudent.end() )
cout<<"在mapStudent中找到了key=3的数据,该数据的内容为: "<<iter->second<<endl;
else
cout<<"没有在mapStudent中找到key=3的数据"<<endl;
mapStudent.insert(map<int, string>::value_type(10, "ten-pierce"));
mapStudent.insert(map<int, string>::value_type(15, "fifteen-pierce"));
cout<<"查找指定数据的第三种方式:使用lower_bound()和upper_bound()以及equal_range函数,这些函数输入参数为Key"<<endl;
iter = mapStudent.lower_bound( 10 ); //返回键值 >= 给定key的第一项的位置iterator
cout<<"处在lower_bound函数的作用范围内: 大于等于10且最靠近10的项是: "<< "<" <<iter->first<<", " <<iter->second <<" >"<<endl;
iter = mapStudent.upper_bound( 10 ); //返回键值 > 给定key的第一项的位置iterator
cout<<"处在upper_bound函数的作用范围内,大于10且最靠近10的项是: "<< "<" <<iter->first<<", " <<iter->second <<" >"<<endl;
pair<map<int,string>::iterator, map<int,string>::iterator> mappair;
mappair = mapStudent.equal_range(12);//equal_range()返回值pair中第一项是lower_bound(2)的返回值iterator,第二项是调用upper_bound(2)的返回值iterator
//显然如果mapStudent中存在指定的key,则mappair的第一项和第二项是不相等的,如果相等,则意味着mapStudent中存在着该项
if (mappair.first == mappair.second)
cout<<"在mapStudent中没有找到给定key=12的项"<<endl;
cout<<endl<<endl;
/*从map中删除元素的三种方式,其实是earse()三种重载方式*/
cout<<"从map中删除元素的第一种方式:iterator erase( iterator it)删除一个迭代器指定的位置,成功删除后返回1,删除失败返回0"<<endl;
if ( mapStudent.erase( mapStudent.find(10))->first )
{
cout<<"mapStudent中<10, ten-pierce>被删除了"<<endl;
cout<<"此时mapStudent中含有的数据为"<<endl;
for (iter = mapStudent.begin(); iter != mapStudent.end(); iter++)
cout<<iter->first<<" &&& "<<iter->second<<endl;
}
cout<<"从map中删除元素的第二种方式:iterator erase( iterator first, iterator last)删除一个范围内所有的对象"<<endl;
//clear()便相当于mapStudent.erase( mapStudent.begin(), mapStudent.end() );
if (mapStudent.erase(mapStudent.find(4), mapStudent.find(15))->first) //删除区间是一个左闭右开的区间
{
cout<<"mapStudent执行区间删除操作成功,此时mapStudent中含有的数据为"<<endl;
for (iter = mapStudent.begin(); iter != mapStudent.end(); iter++)
cout<<iter->first<<" &&& "<<iter->second<<endl;
}
cout<<"从map中删除元素的第三种方式:size_type erase( const Key& key)"<<endl;
mapStudent.insert(map<int, string>::value_type(16, "sixteen pierce"));
if (mapStudent.erase(16) )
{
cout<<"mapStudent定点删除16成功"<<endl;
for (iter = mapStudent.begin(); iter != mapStudent.end(); iter++)
cout<<iter->first<<" &&& "<<iter->second<<endl;
}
cout<<endl<<endl;
/*map中swap函数的用法,但是swap只限于两个内部数据元祖结构相同的map之间互换,map<int, string>和map<int, int>是不能互换的*/
map<int, string> neighborClass;
neighborClass.insert(map<int, string>::value_type(1, "zhang"));
neighborClass.insert(map<int, string>::value_type(2, "xi"));
neighborClass.insert(map<int, string>::value_type(3, "li"));
mapStudent.swap( neighborClass );
cout<<"交换后,mapStudent中的数据为"<<endl;
for (iter = mapStudent.begin(); iter != mapStudent.end(); iter++)
cout<<iter->first<<" &&& "<<iter->second<<endl;
cout<<"交换后,neighborClass中的数据为"<<endl;
for (iter = neighborClass.begin(); iter != neighborClass.end(); iter++)
cout<<iter->first<<" &&& "<<iter->second<<endl;
cout<<endl<<endl;
/*map的核心还是通过红黑树来排序key值,从而实现高效的检索和管理操作,而STL默认是采用升序的,但是这也是建立在key的数据类型支持排序
*在本程序中,mapStudent 为map<int, string>,其key值为int类型,故而可以顺畅地完成排序,可如果key值是一个用户自定义的数据类型呢?
*比如用户自定义了一个结构体ownStruct,这时显然用户在提供自定义结构体时还需要提供该结构体的比较算符"<"重载 或者提供仿函数
*/
map<StudentInfo, int> studentGrade;
StudentInfo info = {90, "peter"};
studentGrade.insert(map<StudentInfo, int>::value_type(info, 2010110111));
info.grade = 95; info.studentName = "kaisen";
studentGrade.insert(map<StudentInfo, int>::value_type(info, 2010110112));
info.grade = 90; info.studentName = "pierce";
studentGrade.insert(map<StudentInfo, int>::value_type(info, 2010110113));
info.grade = 90; info.studentName = "smith";
studentGrade.insert(map<StudentInfo, int>::value_type(info, 2010110114));
info.grade = 79; info.studentName = "black";
studentGrade.insert(map<StudentInfo, int>::value_type(info, 2010110115));
map<StudentInfo, int>::iterator newIter;
for (newIter = studentGrade.begin(); newIter!=studentGrade.end(); newIter++)
cout<<newIter->first.grade<<" "<<newIter->first.studentName<<" "<<newIter->second<<endl;
cout<<endl<<endl;
/*如果结构体并没有提供有效的比较符重载,那么便需要提供仿函数,用以介入相应结构体的比较过程*/
/*看下STL源码中对于map的声明
template <class _Key, class _Tp,
class _Compare __STL_DEPENDENT_DEFAULT_TMPL(less<_Key>),
class _Alloc = __STL_DEFAULT_ALLOCATOR(_Tp) >
class map;
*/
//要是使用自定义的比较函数,使用仿函数时,必须将第三个比较类采用的仿函数类型手动传入,这里是手动传入sort
map<FoodInfo, int, sort> market;
FoodInfo food = {90, "pado"};
market.insert(map<FoodInfo, int>::value_type(food, 133));
food.price = 10; food.vegetables = "tomato";
market.insert(map<FoodInfo, int>::value_type(food, 12));
food.price = 10; food.vegetables = "tofu";
market.insert(map<FoodInfo, int>::value_type(food, 37));
food.price = 40; food.vegetables = "mulk";
market.insert(map<FoodInfo, int>::value_type(food, 71));
map<FoodInfo, int, sort>::iterator foodIter;
for (foodIter = market.begin(); foodIter != market.end(); foodIter++)
cout<<foodIter->first.price<<" "<<foodIter->first.vegetables<<" "<<foodIter->second<<endl;
}
运行结果如下
1. map的遍历方式
2. map中搜索定位具体键值对的方式
3. map的删除键值对的方式
4. swap作用于两个内部键值对数据类型相同的map
5. 采用结构体内部重载方式的结果
6. 采用仿函数自定义比较函数的结果