map和set是两个主要的关联容器,但是底层都是红黑树实现
map
STL中关于map的理解:
map特性是,所有元素都会根据元素的键值自动排序,map的所有元素都是pair(结构体),同时有键值key和实值value。pair的第一个元素被视为键值,第二个元素视为实值,map不允许两个元素拥有相同的键值;
pair的定义:
template<class K,class V>
struct Pair
{
K _first;
V _second;
Pair(const K&key, const V&value)
:_first(key)
, _second(value)
{}
};
一:实现一个简单的字典插入,删除,查找
void TestDic()
{
map<string, string> dict;
//插入
dict.insert(make_pair("insert", "插入"));
dict.insert(make_pair("find", "查找"));
dict.insert(make_pair("erase", "删除"));
PrintfMap(dict);
//查找
map<string, string> ::iterator it = dict.find("find");
if(it ==dict.end())
{
cout << "no found" << endl;
}
else
{
cout << "found" << endl;
}
//删除
map<string, string> ::iterator it1 = dict.find("erase");
if (it1 == dict.end())
{
cout << "no found" << endl;
}
else
{
dict.erase(it1);
}
PrintfMap(dict);
}
二:map统计计数(TopK问题)
如:最喜欢吃的水果:
void TestTopk()
{
//方法一:
map<string, int> CountMap;
string fruits[] = { "苹果", "葡萄", "梨", "香蕉", "葡萄", "苹果", "葡萄", "苹果", "葡萄", "苹果", "苹果", };
for (size_t i = 0; i < sizeof(fruits)/sizeof(fruits[0]); ++i)
{
map<string, int>::iterator ret = CountMap.find(fruits[i]);
if (ret != CountMap.end())
{
ret->second++;
}
else//第一次出现
{
CountMap.insert(make_pair(fruits[i], 1));
}
}
}
方法一的特点效率低,如果不存在,查找了两次;
方法二:
void TestTopk()
{
//调pair<iterator, bool> insert(const value_type& val);
/*
insert 插入成功return pair,return false表明已经有节点存在
*/
/*map<string, int> CountMap;
string fruits[] = { "苹果", "葡萄", "梨", "香蕉", "葡萄", "苹果", "葡萄", "苹果", "葡萄", "苹果", "苹果", };
for (size_t i = 0; i < sizeof(fruits) / sizeof(fruits[0]); ++i)
{
pair<map<string, int> ::iterator, bool> pr = CountMap.insert(make_pair(fruits[i], 1));
if (pr.second == false) //已经存在++计数
{
pr.first->second++;
}
}
}
方法二的比方法一的效率高,只查找了找了一次;
方法三:
void TestTopk()
{
map<string, int> CountMap;
string fruits[] = { "苹果", "葡萄", "梨", "香蕉", "葡萄", "苹果", "葡萄", "苹果", "葡萄", "苹果", "苹果", };
for (size_t i = 0; i < sizeof(fruits) / sizeof(fruits[0]); ++i)
{
CountMap[fruits[i]]++;
}
}
如果前年的两种比较容易理解,那么这种方法会比较那理解,它是如何实现统计计数呢?
调用operator[]来实现
V&operator[](const k&key)
{
return (*((this->insert(make_pair(k,mapped_typed()))).first)).second;
}
我们一层一层来分析:
1:mapped_typed()相当于调用V的缺省构造函数;
2:make_pair是pair的构造函数;里面有两个参数first和second;
3: first表示是迭代器,second表示是value;
4: 插入是insert返回值是pair;*解引用;取里面的数据;
5:如果没有则进行插入,如果存在把&value的值返回;
operator[]应用插入和修改
void PrintfMap(const map<string, string>&dict)
{
map<string, string>::const_iterator it = dict.begin();
while (it!=dict.end())
{
cout << (*it).first << ":" << it->second << endl;
++it;
}
cout << endl;
}
//operator[]的作用是插入和修改实现字典:
void TestMapDict()
{
map<string, string> dict;
dict["sort"] = "排序";
dict["insert"] = "插入";
dict["left"] = "左边";
dict["sort"] = "***";
PrintfMap(dict);
}
前面我们了解到Topk的几种算法,但我们希望统计最喜欢吃的水果的次数,按照排序跟家直观;那么怎样能够快速排序呢?
三:排序的几种常见算法:
在前面我们知道
堆排序,优先级队列;http://blog.csdn.net/f2016913/article/details/68483000
这里我们主要看map和set的实现:
1:map实现排序
void TestTopk()
{
map<string, int> Countmap;
string fruits[] = { "苹果", "葡萄", "梨", "香蕉", "葡萄", "苹果", "葡萄", "苹果", "葡萄", "苹果", "苹果", };
for (size_t i = 0; i < sizeof(fruits) / sizeof(fruits[0]); ++i)
{
Countmap[fruits[i]]++;
}
vector<map<string, int>::iterator> v;
map<string, int>::iterator CountIt = Countmap.begin();
while (CountIt!=Countmap.end())
{
v.push_back(CountIt);
++CountIt;
}
//仿函数
struct Compare
{
bool operator()(map<string, int>::iterator l, map<string, int>::iterator r)
{
return l->second > r->second;
}
};
sort(v.begin(), v.end(), Compare());
}
2:Multimap实现:
void TestMultimap()
{
typedef multimap<string, string> Dict;
typedef multimap<string, string>::iterator DictIt;
Dict dict;
dict.insert(make_pair("sort", "排序"));
dict.insert(make_pair("sort", "排序"));
dict.insert(make_pair("sort", "排序"));
dict.insert(make_pair("left", "左边"));
dict.insert(make_pair("left", "剩余"));
DictIt it = dict.begin();
while (it!=dict.end())
{
cout << it->second << " ";
++it;
}
cout << endl;
}
multimap特性以及用法与map完全相同,唯一的差别在于:
1. 允许重复键值的元素插入容器(使用了RB-Tree的insert_equal函数)
2. 键值key与元素value的映照关系是多对多的关系
3. 没有定义[]操作运算
3:set实现:
set的特性是,所有元素会根据元素的键值自动排序,set元素不想map那样可以同时拥有实值(value)和键值(key),set元素的键值就是实值,set不允许两个相同的键值;
void Testset()
{
set<int> s;
s.insert(1);
s.insert(1);
s.insert(1);
s.insert(1);
s.insert(1);
s.insert(4);
s.insert(2);
s.insert(3);
s.insert(5);
set<int> ::iterator it = s.begin();
while (it!=s.end())
{
cout << *it << " ";
++it;
}
cout << endl;
}
set不仅会排序,而且会去重
4:mulitiset的实现:
mulitise的特性以及用法和set完全相同,唯一的差别是允许键值的重复
void Testmultiset()
{
multiset<int> s;
s.insert(1);
s.insert(1);
s.insert(1);
s.insert(1);
s.insert(1);
s.insert(4);
s.insert(2);
s.insert(3);
s.insert(5);
set<int> ::iterator it = s.begin();
while (it!=s.end())
{
cout << *it << " ";
++it;
}
cout << endl;
}