[c++] Associative Containers

关联容器 和 顺序容器 的本质差别在于: 

关联容器 通过键(key)存储和读取元素,而顺序容器则通过元素在容器中的位置顺序存储和访问元素。

Goto: C++ Primer 第十章 关联容器

  

关联式容器依据特定的排序准则,自动为其元素排序。排序准则以函数形式呈现,用来比较元素值(value)或元素键(key)。缺省情况下以operator<进行 比较,不过你也可以提供自己的比较函数,定义出不同的排序准则。

通常关联式容器 由二叉树(binary tree)实现。在二叉树中,每个元素(节 点)都有一个父节点和两个子节点;左子树的所有元素都比自己小,右子树的所有元素都比自己大。关联式容器的差别主要在于元素的类型以及处理重复元素时的方式。

STL预先定义好的关联式容器有:

  • 集合(set),大小可变的集合,支持通过键实现的快速读取
  • 多重集合(multiset),支持同一个键多次出现的 map 类型
  • 映射(map),关联数组:元素通过键来存储和读取
  • 多重映射(multimap),支持同一个键多次出现的 set 类型

 

 

映射(map)

一、基本操作

int main()
{
    cout << "Hello World!" << endl;

// 初始化方法1 pair
<string, int> author1; author1.first = "lolo"; author1.second = 18;
// 初始化方法2 pair
<string, int> author2; author2 = make_pair("lolo", 18); map<string, int> myMap;

// 初始化方法一 myMap[
"jeff"] = 29;

// 初始化方法二
myMap.insert(make_pair("Bing", 99));
cout
<< myMap["jeff"] << endl; cout << myMap["lolo"] << endl; //Actually, new element added. return 0; }
 

隐患:"被搜索的" 空键值

查找Key不存在的内容,会默认自动添加。

(1) The map contains this key. So it will return the corresponding key value.

(2) The map doesn't contain the key. In this case, it will automatically add a key to the map with null value.

 

方案:键值存在么?

  • 查找键值

找到第一个就返回了。

if ( m.find("f") == m.end() ) {
  // not found
} else {
  // found
}

 

  • 计数键值

需要整个遍历一遍。

if (m.count("f") > 0)
  cout << " is an element of m.\n";
else 
  cout << " is not an element of m.\n";

 

二、key 排序

默认排序

原理类似 Max/Min heap 默认是key排序.自定义排序,则在模板中添加了第三项.自动一

#include<map>  
#include<string>  
#include<iostream>  
using namespace std;  
  
typedef pair<string, int> PAIR;  
  
ostream& operator<<(ostream& out, const PAIR& p) {  
  return out << p.first << "\t" << p.second;  
}  
  
int main() {  
  map<string, int, greater<string>> name_score_map;  
name_score_map[
"LiMin"] = 90; name_score_map["ZiLinMi"] = 79; name_score_map["BoB"] = 92; name_score_map.insert(make_pair("Bing",99)); name_score_map.insert(make_pair("Albert",86));
for (map<string, int>::iterator iter = name_score_map.begin(); iter != name_score_map.end(); ++iter) { cout << *iter << endl; }
return 0; }

 

自定义排序

这里使用了类函数,虽然是结构体,但其实区别不大.

struct CmpByKeyLength {  
  bool operator()(const string& k1, const string& k2) {  
    return k1.length() < k2.length();  
  }  
}; // 使用了函数类,效率更高

int main() {
map
<string, int,
CmpByKeyLength> name_score_map;
name_score_map[
"LiMin"] = 90; name_score_map["ZiLinMi"] = 79; name_score_map["BoB"] = 92; name_score_map.insert(make_pair("Bing",99)); name_score_map.insert(make_pair("Albert",86));
for (map<string, int>::iterator iter = name_score_map.begin(); iter != name_score_map.end(); ++iter) { cout << *iter << endl; }
return 0; }

 

 

集合(set)

插入操作

注意insert后的返回值模板类型.插入后打印,本身的值是排序好的.类似于python中的 bisect模块.

void func_set(void)
{
      std::set<int> myset;
      std::set<int>::iterator it;
// 插入操作后的模板类型:(1) 插入值的指针,(2) 插入成功与否 std::pair<std::set<int>::iterator, bool> ret; // set some initial values: for (int i=1; i<=5; ++i) {
myset.insert(i*10); // set: 10 20 30 40 50
}

// Jeff --> ret.second 插入成功? 原来没有;插入失败?原本就有。 ret = myset.insert(20); if (ret.second==false) {
it=ret.first; // "it" now points to element 20 }

myset.insert (it, 25); // max efficiency inserting myset.insert (it, 24); // max efficiency inserting myset.insert (it, 26); // no max efficiency inserting int myints[]= {5,10,15}; // 10 already in set, not inserted myset.insert (myints, myints+3); std::cout << "myset contains:"; for (it=myset.begin(); it!=myset.end(); ++it) std::cout << ' ' << *it; std::cout << '\n'; }

 

实现原理

c++之stl 二叉树

set由 "二叉搜索树" 实现,并且对树进行了平衡处理,使得元素在树中分部较为均匀,因此能保持搜索、插入、删除的复杂度在O(logn)。

 

multimap

何时有用?

一般不用,用时自己实现还比较麻烦,不得不用这个接口,毕竟自己不想去实现 "红黑树".

How is it useful: Multimaps are one of those things that you need rarely, but when you need them, you really need them.

/*
 *
 ********************************************
 *  multimap多重映照容器的基础说明:
 ********************************************
 *
 * multimap多重映照容器:容器的数据结构采用红黑树进行管理
 * multimap的所有元素都是pair:第一元素为键值(key),不能修改;第二元素为实值(value),可被修改
 *
 * multimap特性以及用法与map完全相同,唯一的差别在于:
 * 允许重复键值的元素插入容器(使用了RB-Tree的insert_equal函数) 
 * 因此:
 * 键值key与元素value的映照关系是多对多的关系
 * 没有定义[]操作运算 
 * 
 * Sorted Associative Container  Pair Associative Container   Unique Associative Container
 *
 * 使用multimap必须使用宏语句#include <map>          
 *
 **************************************************************************************
 *
 * 创建multimap对象:
 * 1.multimap<char,int,greater<char> > a;    //元素键值类型为char,映照数据类型为int,键值的比较函数对象为greater<char>
 * 2.multimap(const key_compare& comp)       //指定一个比较函数对象comp来创建map对象
 * 3.multimap(const multisetr&);             //multimap<int,char*> b(a); //此时使用默认的键值比较函数less<int>
 * 4.multimap(first,last);         
 * 5.multimap(first,last,const key_compare& comp);  
 *
 * //Example:
 * pair<const int ,char> p1(1,'a');
 * pair<const int ,char> p2(2,'b');
 * pair<const int ,char> p3(3,'c');
 * pair<const int ,char> p4(4,'d');
 * pair<const int ,char> pairArray[]={p1,p2,p3,p4};
 * multimap<const int,char> m4(pairArray,pairArray+5);
 * multimap<const int,char> m3(m4);
 * multimap<const int,char,greater<const int> > m5(pairArray,pairArray+5,greater<const int>());
 *
 **************************************************************************************
 *
 * 元素的插入
 * //typedef pair<const key,T> value_type;
 * pair<iterator,bool> insert(const value_type& v);    
 * iterator insert(iterator pos,const value_type& v);
 * void insert(first,last);
 *
 **************************************************************************************
 *
 * 元素的删除
 * void erase(iterator pos);
 * size_type erase(const key_type& k);     //删除等于键值k的元素
 * void erase(first,last);        //删除[first,last)区间的元素
 * void clear();
 *
 **************************************************************************************
 *
 * 访问与搜索
 *
 * iterator begin();iterator end();     //企图通过迭代器改变元素是不被允许的
 * reverse_iterator rbegin();reverse_iterator rend();
 *
 * iterator find(const key_type& k) const;
 * pair<iterator,iterator> equal_range(const key_type& k) const;//返回的pair对象,
 *                //first为lower_bound(k);大于等于k的第一个元素位置
 *                //second为upper_bound();大于k的第一个元素位置
 *
 * 其它常用函数
 * bool empty() const;
 * size_type size() const;
 * size_type count(const key_type& k) const;   //返回键值等于k的元素个数
 * void swap();
 *
 * iterator lower_bound();iterator upper_bound();pair<iterator,iterator> equal_range();//上界、下届、确定区间
 *
 *
 *
 ********************************************
 **   cumirror ** tongjinooo@163.com **    **
 ********************************************
 *
 */

#include <map>
#include <string>
#include <iostream>

// 基本操作与set类型,牢记map中所有元素都是pair
// 对于自定义类,初学者会觉得比较函数如何构造很麻烦,这个可以参照前面的书写示例
// 但若设置键值为int或char类型,无须构造比较函数

struct student{
 char* name;
 int age;
 char* city;
 char* phone;
};

int main()
{
     using namespace std;

     student s[]={
      {"童进",23,"武汉","XXX"},
      {"老大",23,"武汉","XXX"},
      {"饺子",23,"武汉","XXX"},
      {"王老虎",23,"武汉","XXX"},
      {"周润发",23,"武汉","XXX"},
      {"周星星",23,"武汉","XXX"}
     };
      pair<int,student> p1(4,s[0]);
      pair<int,student> p2(2,s[1]);
      pair<int,student> p3(3,s[2]);
      pair<int,student> p4(4,s[3]);  //键值key与p1相同
      pair<int,student> p5(5,s[4]);
      pair<int,student> p6(6,s[5]);
     multimap<int, student> a;
     a.insert(p1);
     a.insert(p2);
     a.insert(p3);
     a.insert(p4);
     a.insert(p5);
     a.insert(p6); // 会有重复的key
     typedef multimap<int, student>::iterator int_multimap;
     pair<int_multimap,int_multimap> p = a.equal_range(4);
     int_multimap i = a.find(4);
     cout<<"班上key值为"<< i->first<<"的学生有:"<<a.count(4)<<"名,"<<"   他们是:"<<endl;
     for(int_multimap k = p.first; k != p.second; k++)
     {
        cout<<k->second.name<<endl;
     }
     cout<<"删除重复键值的同学"<<endl;
     a.erase(i);
     cout<<"现在班上总人数为:"<<a.size()<<".   人员如下:"<<endl;
     for(multimap<int,student>::iterator j=a.begin(); j != a.end(); j++)
     {      
          cout<<"The name: "<<j->second.name<<"      "<<"age: "<<j->second.age<<"   "
           <<"city: "<<j->second.city<<"      "<<"phone: "<<j->second.phone<<endl;
     }

     return 0;
}
View Code

 

 

End.

posted @ 2016-12-01 18:33  郝壹贰叁  阅读(450)  评论(0编辑  收藏  举报