文章目录
一、关联式容器
1. 定义
set, multiset, map, multimap 是一种非线性的树结构,具体的说采用的是一种比较高效的特殊的平衡检索二叉树—— 红黑树结构。
- set ,又称集合,实际上就是一组元素的集合,但其中所包含的元素的值是唯一的,且是按一定顺序排列的,集合中的每个元素被称作集合中的实例。因为其内部是通过链表的方式来组织,所以在插入的时候比vector 快,但在查找和末尾添加上被vector 慢
- multiset ,是多重集合,其实现方式和set 是相似的,只是它不要求集合中的元素是唯一的,也就是说集合中的同一个元素可以出现多次。
- map ,提供一种“键- 值”关系的一对一的数据存储能力。其“键”在容器中不可重复,且按一定顺序排列(其实我们可以将set 也看成是一种键- 值关系的存储,只是它只有键没有值。它是map 的一种特殊形式)。由于其是按链表的方式存储,它也继承了链表的优缺点。
- multimap , 和map 的原理基本相似,它允许“键”在容器中可以不唯一。
2. 特点
-
其内部实现是采用非线性的二叉树结构,具体的说是红黑树的结构原理实现的
-
set 和map 保证了元素的唯一性,mulset 和mulmap 扩展了这一属性,可以允许元素不唯一
-
元素是有序的集合,默认在插入的时候按升序排列
3. 对比顺序性容器
-
关联容器对元素的插入和删除操作比vector 要快,因为vector 是顺序存储,而关联容器是链式存储;比list 要慢,是因为即使它们同是链式结构,但list 是线性的,而关联容器是二叉树结构,其改变一个元素涉及到其它元素的变动比list 要多,并且它是排序的,每次插入和删除都需要对元素重新排序;
-
关联容器对元素的检索操作比vector 慢,但是比list 要快很多。vector 是顺序的连续存储,当然是比不上的,但相对链式的list 要快很多是因为list 是逐个搜索,它搜索的时间是跟容器的大小成正比,而关联容器 查找的复杂度基本是Log(N) ,比如如果有1000 个记录,最多查找10 次,1,000,000 个记录,最多查找20 次。容器越大,关联容器相对list 的优越性就越能体现;
-
在使用上set 区别于vector,deque,list 的最大特点就是set 是内部排序的,这在查询上虽然逊色于vector ,但是却大大的强于list 。
-
在使用上map 的功能是不可取代的,它保存了“键- 值”关系的数据,而这种键值关系采用了类数组的方式。数组是用数字类型的下标来索引元素的位置,而map 是用字符型关键字来索引元素的位置。在使用上map 也提供了一种类数组操作的方式,即它可以通过下标来检索数据,这是其他容器做不到的,当然也包括set 。(STL 中只有vector 和map 可以通过类数组的方式操作元素,即如同ele[1] 方式)
二、set和multiset容器
1. 基本概念
简介:
- 所有元素都会在插入时自动被排序
本质:
- set/multiset属于关联式容器, 底层结构是用二叉树实现
set和multiset区别:
- set不允许容器中有重复的元素
- multiset允许容器中有重复的元素
2. set构造和赋值
构造:
set<T> st;
// 默认构造set(const set& st);
// 拷贝构造
赋值:
set& operator=(const set& st);
#include<iostream>
#include<set>
using namespace std;
void printset(set<int>& s)
{
for(set<int>::iterator it = s.begin(); it != s.end(); it++)
{
cout << *it << " ";
}
cout << endl;
}
void test()
{
set<int> s;
// 插入数据 只有insert方式
s.insert(10);
s.insert(30);
s.insert(20);
s.insert(50);
s.insert(10);
// 遍历容器
// set容器特点:所有元素插入时自动被排序
// set容器不允许插入重复值,如果有重复的数,会被忽略
printset(s); // 10 20 30 50
}
3. set大小和交换
函数原型;
size();
// 返回容器中元素的数目empty();
// 判断容器是否为空swap(st);
// 交换两个集合容器
4. set插入和删除
函数原型:
insert(elem);
// 在容器中插入元素clear();
// 清除所有元素erase(pos);
// 删除pos迭代器所指的元素,返回下一个元素的迭代器erase(beg, end);
// 删除区间【beg, end)的所有元素,返回下一个元素的迭代器erase(elem);
// 删除容器中值为elem的元素
5. set查找和统计
函数原型:
find(key);
// 查找key是否存在,若存在,返回该键的元素的迭代器;若不存在,返回set.end();count(key);
// 统计key的元素个数
set<int> s;
// 插入数据 只有insert方式
s.insert(10);
s.insert(30);
s.insert(20);
s.insert(50);
s.insert(10);
set<int>::iterator pos = s.find(30);
if(pos != s.end())
{
cout<< "找到元素: " << *pos << endl;
}
else{
cout << "没找到" << endl;
}
int num = s.count(10);
// 对于set而已,统计结果要么是0,要么是1,因为没有重复的数
cout << " num = " << num << endl;
6. set和multiset区别
区别:
- set不可以插入重复数据,而multiset可以
- set插入数据的同时会返回插入结果,表示插入成功
- multiset不会检测数据,因此可以插入重复数据
set<int> s;
// 接受插入后返回的结果
pair<set<int>::iterator, bool> ret = s.insert(10);
if(ret.second) // 表示第二个boll
{
cout << "第一次插入成功" << endl;
}else{
cout << "第一次插入失败" << endl;
}
ret = s.insert(10);
if(ret.second)
{
cout << "第二次插入成功" << endl;
}else{
cout << "第二次插入失败" << endl;
}
// multiset
multiset<int> ms;
ms.insert(10);
ms.insert(10);
总结:
- 如果不允许插入重复数据可以利用set
- 如果需要插入重复数据利用multiset
7. pair对组创建
功能描述:
- 成对出现的数据,利用对组可以返回两个数据
两种创建方式:
pair<type, type> p (value1, value2);
pair<type, type> p = make_pair(value1, value2);
- 用
p.first
和p.second
访问
// 创建对组
pair<string, int> p("tom", 20);
cout << "姓名: " << p.first
<< " 年龄: " << p.second
<< endl;
// 另一种方式
pair<string, int> p2 = make_pair("jery", 30);
cout << "姓名: " << p2.first
<< " 年龄: " << p2.second
<< endl;
8. set容器排序
学习目标:
- set容器默认排序规则为从小到大,掌握如何改变排序规则
主要技术点:
- 利用仿函数,可以改变排序规则
- 仿函数:在一个类中重载()
示例一:set存放内置数据结构
// 仿函数
class MyCompare
{
public:
bool operator()(int v1, int v2)
{
return v1>v2;
}
};
void test()
{
set<int> s1;
s1.insert(10);
s1.insert(40);
s1.insert(30);
s1.insert(15);
for(set<int>::iterator it = s1.begin(); it != s1.end(); it++)
{
cout << *it << " ";
}
cout << endl; // 10 15 30 40
// 指定排序规则为从大到小, 利用仿函数
set<int, MyCompare> s2;
s2.insert(10);
s2.insert(40);
s2.insert(30);
s2.insert(15);
for(set<int, MyCompare>::iterator it1 = s2.begin(); it1 != s2.end(); it1++)
{
cout << *it1 << " ";
}
cout << endl; //40 30 15 10
}
示例二: set存放自定义数据类型
对于自定义数据类型,set必须指定排序规则才可以插入数据
class Person
{
public:
Person(string name, int age)
{
this->m_age = age;
this->m_name = name;
}
string m_name;
int m_age;
};
// 仿函数
class MyCompare
{
public:
bool operator()(const Person& p1, const Person& p2)
{
// 按照年龄来排序
return p1.m_age > p2.m_age;
}
};
void test()
{
// 创建对象
Person p1("刘备", 27);
Person p2("张飞", 22);
Person p3("关羽", 25);
Person p4("赵云", 23);
// 创建容器
set<Person, MyCompare> s;
s.insert(p1);
s.insert(p2);
s.insert(p3);
s.insert(p4);
for(set<Person, MyCompare>::iterator it = s.begin(); it != s.end(); it++)
{
cout << "姓名: " << it->m_name
<< " 年龄: " << it->m_age
<< endl;
}
}
三、map/multimap容器
1. map基本概念
简介:
- map中所有元素都是pair
- pair中第一个元素为key(键值),起到索引作用,第二个元素为value(实值)
- 所有元素都会根据元素的key(键值)自动排序
本质:
- map/multimap属于关联式容器,底层结构是用二叉树实现
优点:
- 可以根据key值快速找到value值
map和multimap区别:
- map不允许容器中有重复key值元素
- multimap允许容器中有重复key值元素
2. map构造和赋值
构造:
map<T1, T2> mp;
// 默认构造函数map(const map& mp);
// 拷贝构造函数
赋值:
map& operator=(const map& mp);
void PrintMap(map<int,int>& m)
{
for(map<int,int>::iterator it = m.begin(); it != m.end(); it++)
{
cout << "key: " << it->first << " value: " << it->second << endl;
}
}
void test()
{
map<int, int> m; // 默认构造
m.insert(pair<int,int>(1,10));
m.insert(pair<int,int>(3,20));
m.insert(pair<int,int>(2,40));
m.insert(pair<int,int>(4,50));
// m.insert(make_pair(5,40));
PrintMap(m);
map<int,int> m2(m); // 拷贝构造
PrintMap(m2);
map<int,int> m3;
m3 = m2; // 赋值
PrintMap(m3);
}
3. map大小和交换
函数原型:
size();
// 返回容器中元素的数目empty();
// 判断容器是否为空swap(st);
// 交换两个集合容器
4. map插入和删除
函数原型:
insert(elem);
// 插入元素clear();
// 清除所有元素erase(pos);
// 删除pos迭代器所指的元素,返回下一个元素的迭代器erase(beg, end);
// 删除区间【beg, end)的所有元素,返回下一个元素的迭代器erase(key);
// 删除容器中值为key的元素- 可以通过【key】访问value
5. map查找和统计
函数原型:
find(key);
// 查找key是否存在,若存在,返回该键的元素的迭代器;若不存在,返回map.end();count(key);
// 统计key的元素个数
map<int, int> m; // 默认构造
m.insert(pair<int,int>(1,10));
m.insert(pair<int,int>(3,20));
m.insert(pair<int,int>(2,40));
m.insert(pair<int,int>(4,50));
map<int, int>::iterator pos = m.find(3);
if(pos != m.end())
{
cout << "查到了元素 key = " << pos->first << " value = " << pos->second << endl;
}else{
cout << "没查到" << endl;
}
// map不允许插入重复key元素,count统计而言 结果要么是0 要么是1
int num = m.count(3);
cout << "num = " << num << endl;
6. map容器排序
学习目标:
- map容器默认排序规则为 按照key值进行 从小到大排序, 掌握如何改变排序规则
主要技术点:
- 利用仿函数,可以改变排序规则
// 仿函数
class MyCompare
{
public:
bool operator()(int v1, int v2)
{
return v1>v2;
}
};
void PrintMap(map<int,int, MyCompare>& m)
{
for(map<int,int, MyCompare>::iterator it = m.begin(); it != m.end(); it++)
{
cout << "key: " << it->first << " value: " << it->second << endl;
}
}
void test()
{
map<int, int, MyCompare> m; // 默认构造
m.insert(pair<int,int>(1,10));
m.insert(pair<int,int>(3,20));
m.insert(pair<int,int>(2,40));
m.insert(pair<int,int>(4,50));
PrintMap(m);
}
7. 案例-员工分组
1. 案例描述
- 公司今天招聘了10个员工(ABCDEFGHIJ),10名员工进入公司之后,需要指派员工在哪个部门工作
- 员工信息有:姓名 工资组成; 部门分为:策划、美术、研发
- 随机给10个员工分配部门和工资
- 通过multimap进行信息的插入 key(部门编号) value(员工)
- 分部门显示员工信息
2. 实现步骤
- 创建10名员工,放到vector中
- 遍历vector容器,取出每个员工,进行随机分组
- 分组后,将员工部门编号作为key,具体员工作为value,放入到multimap容器中
- 分部门显示员工信息
3. 案例代码
#include<iostream>
#include<vector>
#include<map>
#include<string>
using namespace std;
#define CEHUA 0
#define MEISHU 1
#define YANFA 2
class Worker
{
public:
string m_name;
int m_salary;
};
// 创建员工
void createWorker(vector<Worker>& v)
{
string nameSeed = "ABCDEFGHIJ";
for(int i=0; i<10; i++)
{
Worker worker;
worker.m_name = "员工";
worker.m_name += nameSeed[i];
worker.m_salary = rand()%10000 + 10000; // 10000~19999
v.push_back(worker);
}
}
// 员工分组
void setGroup(vector<Worker>& v, multimap<int, Worker>& m)
{
for(vector<Worker>::iterator it = v.begin(); it != v.end(); it++)
{
// 产生随机部门编号
int deptID = rand() % 3; // 0 1 2
// 将员工插入到分组中
// key部门编号,value具体员工
m.insert(pair<int, Worker>(deptID, *it));
}
}
void showWorkerByGrourp(multimap<int, Worker>& m)
{
cout << "策划部门:" << endl;
multimap<int, Worker>::iterator pos = m.find(CEHUA);
int count = m.count(CEHUA);
int index = 0;
for(; pos != m.end() && index < count; pos++, index++)
{
cout << "姓名:" << pos->second.m_name << "工资: " << pos->second.m_salary << endl;
}
}
int main()
{
// 1.创建员工
vector<Worker> vWorker;
createWorker(vWorker);
// 测试
for(vector<Worker>::iterator it = vWorker.begin(); it != vWorker.end(); it++)
{
cout << "姓名:" << it->m_name
<< " 工资: " << it->m_salary
<< endl;
}
// 2. 员工分组
multimap<int, Worker> mWorker;
setGroup(vWorker, mWorker);
// 3. 分组显示员工
showWorkerByGrourp(mWorker);
return 0;
}