当前位置:   article > 正文

从0到1入门C++编程——10 stack容器、queue容器、list容器、set容器、map容器_c++容器

c++容器

一、stack容器

stack是一种后进先出的数据结构,栈中只有栈顶元素才可以被外界使用,所以栈不允许有遍历行为。
栈可以进行判空操作,也可以统计栈中的元素数量。
stack容器常用的接口有以下几个。

//构造函数
stack<T> stk;  //默认构造函数
stack(const stack &stk);  //拷贝构造函数
//赋值操作
stack& operator=(const stack &stk);  //=赋值操作
//数据存取
push(elem);  //向栈顶添加元素
pop();  //删除栈顶元素
top();  //返回栈顶元素
//大小操作
empty();  //判空
size();  //返回栈的大小
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

关于栈容器的简单操作示例如下。

#include <iostream>
#include <stack>
using namespace std;

void fun()
{
	stack<int> s;
	s.push(1);
	s.push(2);
	s.push(3);
	s.push(4);
	cout<<"栈的大小为:"<<s.size()<<endl;
	while(!s.empty())
	{
		cout<<"栈顶元素:"<<s.top()<<endl;
		s.pop();  //出栈
	}
	cout<<"栈的大小为:"<<s.size()<<endl;
}

int main()
{
	fun();
	system("pause");
	return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26

上面程序的运行结果如下图所示。
在这里插入图片描述


二、queue容器

queue是一种先进先出的数据结构,queue容器允许从一端新增元素,从另一端移除元素。队列中只有队头和队尾才可以被外界使用,因此队列不允许有遍历行为。
queue容器常用的接口有以下几个。

//构造函数
queue<T> que;  //默认构造函数
queue(const queue &que);  //拷贝构造函数
//赋值操作
queue& operator=(const queue &que);  //=赋值操作
//数据存取
push(elem);  //往队尾添加元素
pop();  //删除队头元素
back();  //返回最后一个元素
front();  //返回第一个元素
//大小操作
empty();  //判空
size();  //返回队列的大小
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

关于队列容器的简单操作示例如下。

#include <iostream>
#include <queue>
using namespace std;

void fun()
{
	queue<int> q;
	q.push(1);
	q.push(2);
	q.push(3);
	q.push(4);
	cout<<"队列的大小为:"<<q.size()<<endl;
	while(!q.empty())
	{
		cout<<"队头元素:"<<q.front()<<endl;
		cout<<"队尾元素:"<<q.back()<<endl;
		q.pop();
	}
	cout<<"队列的大小为:"<<q.size()<<endl;
}

int main()
{
	fun();
	system("pause");
	return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27

三、list容器

链表由一系列结点组成,结点由存储数据元素的数据域和存储下一个结点地址的指针域构成。
链表将数据进行链式存储,是一种物理存储单元上非连续的存储结构,数据元素的逻辑顺序是通过链表中的指针链接实现的。STL中的链表是一个双向循环链表。
链表的优点是其采用动态存储分配,不会造成内存的浪费和溢出,而且链表执行插入和删除操作十分方便,只需要修改指针,不需要移动大量元素。缺点是遍历的速度没有数组快,因为数据不是连续存放的,此外,链表占用的空间也比数组大,相比于数组只存放数据,链表还需要存放地址。
在这里插入图片描述
由于链表的存储方式并不是连续的内存空间,因此链表list中的迭代器只支持前移和后移操作,也就是只能做++和–运算,不能一次跳几个,其属于双向迭代器。
list容器有一个重要的性质,插入和删除操作都不会造成原有list迭代器的失效。这在vector中是不成立的,如果将vector的容量进行扩充,那么其迭代器就会失效。

1、构造函数

list容器常用的接口有以下几个。

list<T> lst;  //默认构造函数
list(begin,end);  //将区间元素进行拷贝
list(n,elem);   //将n个元素进行拷贝
list(const list &lst);  //拷贝构造函数
  • 1
  • 2
  • 3
  • 4

关于list容器构造函数的简单代码示例如下。

#include <iostream>
#include <list>
using namespace std;

void printList(const list<int> &L)
{
	for(list<int>::const_iterator i=L.begin();i!=L.end();i++)
	{
		cout<<*i<<" ";
	}
	cout<<endl;
}

void fun()
{
	list<int> L1;  //默认构造
	L1.push_back(1);
	L1.push_back(2);
	L1.push_front(3);
	L1.push_front(4);
	printList(L1);
	list<int> L2(L1.begin(),L1.end());  //复制区间构造
	printList(L2);
	list<int> L3(5,6);  //指定长度和元素构造
	printList(L3);
	list<int> L4(L3);  //拷贝构造
	printList(L4);
}

int main()
{
	fun();
	system("pause");
	return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35

2、赋值和交换

与前面介绍过的容器一样,list容器赋值包括=赋值和assign赋值,交换仍然使用swap()函数。
关于list容器赋值和交换的简单示例如下。

#include <iostream>
#include <list>
using namespace std;

void printList(const list<int> &L)
{
	for(list<int>::const_iterator i=L.begin();i!=L.end();i++)
	{
		cout<<*i<<" ";
	}
	cout<<endl;
}

void fun()
{
	list<int> L1;
	L1.push_back(1);
	L1.push_back(2);
	L1.push_front(3);
	L1.push_front(4);
	cout<<"L1 : ";
	printList(L1);
	list<int> L2;
	L2 = L1;  // = 赋值
	cout<<"L2 : ";
	printList(L2);
	list<int> L3;
	L3.assign(L2.begin(),L2.end());   //assign区间赋值
	cout<<"L3 : ";
	printList(L3);
	list<int> L4;
	L4.assign(6,6);   //指定数字赋值
	cout<<"L4 : ";
	printList(L4);
	L1.swap(L4);  //使用swap()交换两个容器
	cout<<"交换L1和L4后:"<<endl;
	cout<<"L1 : ";
	printList(L1);
	cout<<"L4 : ";
	printList(L4);
}

int main()
{
	fun();
	system("pause");
	return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48

上面程序运行后的结果如下图所示。
在这里插入图片描述

3、大小及判空

list容器的大小及判空操作与前面介绍的容器一样,简单的代码示例如下。

void fun()
{
	list<int> L1;
	L1.push_back(1);
	L1.push_back(2);
	L1.push_front(3);
	L1.push_front(4);
	printList(L1);
	if(!L1.empty())
	{
		cout<<"容器不为空!"<<endl;
		cout<<"容器中的元素个数:"<<L1.size()<<endl;
	}
	L1.resize(7);
	printList(L1);
	L1.resize(10,6);
	printList(L1);
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18

4、插入和删除

插入和删除的主要函数接口有以下几个。

push_front(elem);  //在容器头部插入一个数据
push_back(elem);  //在容器尾部插入一个数据
pop_front();  //从容器头部删除一个数据
pop_back();  //从容器尾部删除一个数据

insert(pos,elem);  //在指定位置插入指定元素,要用迭代器,不能用数字索引
insert(pos,n,elem);  //在指定位置插入n个指定元素
insert(pos,start_pos,end_pos); //从指定位置开始插入区间数据
clear(); //清空数据
erase(pos); //删除指定位置的数据
erase(start_pos,end_pos);  //删除指定区间的数据
remove(elem);  //删除容器中所有和elem值匹配的元素
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

关于插入和删除的操作,list容器中多了一个remove操作,简单的示例如下。

#include <iostream>
#include <list>
using namespace std;

void printList(const list<int> &L)
{
	for(list<int>::const_iterator i=L.begin();i!=L.end();i++)
	{
		cout<<*i<<" ";
	}
	cout<<endl;
}

void fun()
{
	list<int> L1;
	L1.push_back(1);
	L1.push_front(2);
	printList(L1);
	L1.insert(L1.begin(),3);
	L1.insert(++L1.begin(),4,6);
	printList(L1);
	L1.erase(--L1.end());
	printList(L1);
	L1.remove(6);   //移除容器中所有的6
	printList(L1);
}

int main()
{
	fun();
	system("pause");
	return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34

上面程序运行后的结果如下图所示。
在这里插入图片描述

5、数据存取

list容器不同于vector和deque容器,其数据存取不支持中括号[]和at()方式,只能使用front()和back()返回第一个和最后一个元素。
关于list容器数据存取操作的简单代码示例如下。

void fun()
{
	list<int> L1;
	L1.push_back(1);
	L1.push_front(2);
	L1.push_back(3);
	L1.push_front(4);
	printList(L1);
	cout<<"第一个元素为: "<<L1.front()<<endl;
	cout<<"最后一个元素为: "<<L1.back()<<endl;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

验证迭代器是不是支持随机访问且是双向的。

list<int>::iterator i=L.begin();
i++;
i--;  //i++和i--都不报错就是支持双向访问
i = i+1;  //报错就说明不支持随机访问
  • 1
  • 2
  • 3
  • 4

6、反转和排序

list容器的反转和排序分别使用下面的函数。

reverse();  //反转链表
sort();   //将链表进行排序,成员函数,非全局函数
  • 1
  • 2

由于迭代器在list容器中不可以随机访问,因此不能使用标准的算法,但是内部会提供一些函数接口,使用.访问,而不是全局函数。
list容器的反转和排序简单示例如下。

#include <iostream>
#include <algorithm>
#include <list>
using namespace std;

void printList(const list<int> &L)
{
	for(list<int>::const_iterator i=L.begin();i!=L.end();i++)
	{
		cout<<*i<<" ";
	}
	cout<<endl;
}

bool myrule(int a,int b)  //排序规则
{
	return a > b;
}

void fun()
{
	list<int> L1;
	L1.push_back(2);
	L1.push_back(1);
	L1.push_back(4);
	L1.push_back(3);
	cout<<"原序列 :";
	printList(L1);
	L1.reverse();
	cout<<"原序列反转后 :";
	printList(L1);
	L1.sort();  //默认升序排列
	cout<<"原序列升序排列后 :";
	printList(L1);
	L1.sort(myrule);  //降序排列
	cout<<"原序列降序排列后 :";
	printList(L1);
}

int main()
{
	fun();

	system("pause");
	return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46

上面程序运行后的结果如下图所示。
在这里插入图片描述

7、排序案例

将自定义数据类型Person进行排序,排序按照年龄升序排列,如果年龄相同则按照身高降序排列。Person中的属性包括姓名、年龄、身高。
该案例的代码实现如下。

#include <iostream>
#include <algorithm>
#include <list>
#include <string>
using namespace std;

class Person
{
public:
	Person(string name,int age,int height)
	{
		this->name = name;
		this->age = age;
		this->height = height;
	}
	string name;
	int age;
	int height;
};

void printList(const list<Person> &L)
{
	for(list<Person>::const_iterator i=L.begin();i!=L.end();i++)
	{
		cout<<"姓名: "<<(*i).name<<" 年龄: "<<(*i).age<<" 身高: "<<(*i).height<<endl;
	}
}

bool sortPerson(Person &p1,Person &p2)  //排序规则
{
	if(p1.age == p2.age)
	{
		return p1.height > p2.height;  //在年龄相同时按照身高降序
	}
	return p1.age<p2.age;  //按照年龄升序排列
}

void fun()
{
	Person p1("A",20,185);
	Person p2("B",20,175);
	Person p3("C",20,176);
	Person p4("D",18,191);
	Person p5("E",23,186);
	list<Person> L;
	L.push_back(p1);
	L.push_back(p2);
	L.push_back(p3);
	L.push_back(p4);
	L.push_back(p5);
	cout<<"------------排序前------------"<<endl;
	printList(L);
	L.sort(sortPerson);
	cout<<"------------排序后------------"<<endl;
	printList(L);
}

int main()
{
	fun();

	system("pause");
	return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64

上面程序运行后的结果如下图所示。
在这里插入图片描述
本案例中涉及到对自定义数据类型的排序,需要自己指定相应的排序规则,然后传入sort()函数中即可。


四、set/multiset容器

set/multiset容器的特点是所有元素在插入时自动被排序,容器的本质是关联式容器,底层结构用二叉树实现。
set容器和multiset容器的区别在于,set容器不允许容器中有重复的元素,而multiset容器中允许出现重复的元素。
set/multiset容器插入数据时没有push()或push_back(),只能用insert()函数来插入数值。

1、构造和赋值

构造包括默认构造和拷贝构造,赋值通过=实现。
set容器关于构造和赋值的操作示例如下。

#include <iostream>
#include <set>
using namespace std;

void printSet(const set<int> &s)
{
	for(set<int>::const_iterator i=s.begin();i!=s.end();i++)
	{
		cout<<*i<<" ";
	}
	cout<<endl;
}

void fun()
{
	set<int> s1;    //默认构造
	s1.insert(2);  //插入数据用insert()
	s1.insert(1);
	s1.insert(2);
	s1.insert(4);
	s1.insert(2);
	s1.insert(3);
	cout<<"s1 : ";
	printSet(s1);
	set<int> s2(s1);  //拷贝构造
	cout<<"s2 : ";
	printSet(s2);
	set<int> s3;
	s3 = s1;  //赋值操作
	cout<<"s3 : ";
	printSet(s3);
}

int main()
{
	fun();
	system("pause");
	return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39

上面程序运行后的结果如下图所示。
在这里插入图片描述
可以看到,在set容器中插入重复数据虽然不会报错,但也不会打印出来,而且插入无序的元素在打印的时候被排好序了。

2、大小和交换

set容器不支持重新定义容器大小,即没有resize()函数。
关于set容器大小、判空和交换的简单应用如下。

#include <iostream>
#include <set>
using namespace std;

void printSet(const set<int> &s)
{
	for(set<int>::const_iterator i=s.begin();i!=s.end();i++)
	{
		cout<<*i<<" ";
	}
	cout<<endl;
}

void fun()
{
	set<int> s1;
	s1.insert(2);
	s1.insert(1);
	s1.insert(4);
	s1.insert(3);
	cout<<"s1 : ";
	printSet(s1);
	if(!s1.empty())
	{
		cout<<"容器非空,大小为: "<<s1.size()<<endl;
	}
	set<int> s2;
	s2.insert(20);
	s2.insert(10);
	s2.insert(40);
	s2.insert(30);
	cout<<"s2 : ";
	printSet(s2);
	cout<<"交换s1和s2容器后: "<<endl;
	s1.swap(s2);
	cout<<"s1 : ";
	printSet(s1);
	cout<<"s2 : ";
	printSet(s2);
}

int main()
{
	fun();

	system("pause");
	return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48

上面程序运行后的结果如下图所示。
在这里插入图片描述

3、插入和删除

set容器插入使用insert()函数,删除使用erase()函数,清空容器使用clear()函数。在使用erase()函数时,既可以传入迭代器的位置,也可以传入要删除的元素,当然也可以传入一个区间进行删除,传入迭代器的首尾位置,其作用和clear()函数一样。
关于set容器插入和删除的简单操作示例如下。

void fun()
{
	set<int> s1;
	s1.insert(2);
	s1.insert(1);
	s1.insert(4);
	s1.insert(3);
	printSet(s1);
	s1.erase(s1.begin());  //指定迭代器的位置进行删除
	printSet(s1);
	s1.erase(3);  //指定容器中的元素进行删除
	printSet(s1);
	s1.clear();  //清空容器,等价于 s1.erase(s1.begin(),s1.end());
	printSet(s1);
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

4、查找和统计

set容器中进行查找使用的函数是find(elem),查找元素elem是否存在,其返回值是一个迭代器,如果该元素存在,就返回该元素的迭代器,如果不存在,就返回end()。
set容器中统计元素的个数使用的函数是count(elem),但是其结果只能是0或1,对于multiset容器而言,count(elem)的结果可能大于1。

find(elem);  //返回的是迭代器的位置
count(elem);  //返回元素个数
  • 1
  • 2

关于set容器查找和统计的代码示例如下。

#include <iostream>
#include <set>
using namespace std;

void printSet(const set<int> &s)
{
	for(set<int>::const_iterator i=s.begin();i!=s.end();i++)
	{
		cout<<*i<<" ";
	}
	cout<<endl;
}

void fun()
{
	set<int> s1;
	s1.insert(2);
	s1.insert(1);
	s1.insert(2);
	s1.insert(2);
	s1.insert(4);
	s1.insert(3);
	printSet(s1);
	set<int>::iterator pos = s1.find(2);
	if(pos!=s1.end())
	{
		cout<<"元素"<<*pos<<"存在!"<<endl;
		cout<<"元素"<<*pos<<"的个数: "<<s1.count(*pos)<<endl;
	}
	else
	{
		cout<<"元素不存在!"<<endl;
	}
}

int main()
{
	fun();

	system("pause");
	return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42

上面程序运行后的结果如下图所示。
在这里插入图片描述

5、set和multiset的区别

set不可以插入重复数据,其在插入的同时会返回插入结果,反馈是否插入成功,multiset不会检测数据,所以允许插入重复的数据。
set使用插入insert()时,转到其定义如下,再转到Pairib的定义。

template<class _Valty>
		_Pairib insert(_Valty&& _Val)
		{	// try to insert node with value _Val, favoring right side
		return (_Linsert(this->_Buynode(_STD forward<_Valty>(_Val)),
			false));
		}

typedef pair<iterator, bool> _Pairib;   //pair是对组,返回的不仅有迭代器,还有bool类型,即插入成功与否
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

multiset使用插入insert()时,转到其定义如下。

template<class _Valty>
		iterator insert(_Valty&& _Val)
		{	// insert a {key, mapped} value
		return (_Mybase::insert(_STD forward<_Valty>(_Val)).first);
		}
  • 1
  • 2
  • 3
  • 4
  • 5

可以看到,multiset在插入时返回值是一个迭代器类型,不像set容器返回的是pair对组,因此在插入时不做判断,即使元素重复也能插入成功。
关于set和multiset容器的区别,简单的代码示例如下。

#include <iostream>
#include <set>
using namespace std;

void printSet(const multiset<int> &ms)
{
	for(multiset<int>::const_iterator i=ms.begin();i!=ms.end();i++)
	{
		cout<<*i<<" ";
	}
	cout<<endl;
}

void fun1()
{
	set<int> s;
	pair<set<int>::iterator,bool> ret = s.insert(2);
	if(ret.second)
	{
		cout<<"元素第一次插入成功!"<<endl;
	}
	else
	{
		cout<<"元素第一次插入失败!"<<endl;
	}
	ret = s.insert(2);
	if(ret.second)
	{
		cout<<"元素第二次插入成功!"<<endl;
	}
	else
	{
		cout<<"元素第二次插入失败!"<<endl;
	}
}

void fun2()   // multiset容器测试
{
	multiset<int> ms;
	ms.insert(2);
	ms.insert(2);
	ms.insert(2);
	printSet(ms);
}

int main()
{
	fun1();
	fun2();
	system("pause");
	return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52

上面程序运行后的结果如下图所示。
在这里插入图片描述

6、pair对组的创建

对组是指成对出现的数据,利用对组可以返回两个数据。
对组的两种创建方式如下。

pair<type,type> p(value1,value2);
pair<type,type> p = make_pair(value1,value2);
  • 1
  • 2

关于对组的简单应用代码如下。

#include <iostream>
#include <string>
#include <set>
using namespace std;

void fun()
{
	pair<string,int> p1("Tom",10);  //方式一
	cout<<"name: "<<p1.first<<"  age: "<<p1.second<<endl;
	pair<string,int> p2 = make_pair("Jack",20);   //方式二
	cout<<"name: "<<p2.first<<"  age: "<<p2.second<<endl;
}

int main()
{
	fun();
	system("pause");
	return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19

上面程序运行后的结果如下图所示。
在这里插入图片描述

7、排序及规则改变

set容器默认排序规则为从小到大,使用仿函数就可以改变排序规则。
使用仿函数改变内置数据类型排序规则的代码示例如下。

#include <iostream>
#include <string>
#include <set>
using namespace std;

class Compare
{
public:
	bool operator()(int a,int b)  //重载()
	{
		return a > b;
	}
};

void printSet(set<int,Compare> &s)
{
	for(set<int,Compare>::iterator i=s.begin();i!=s.end();i++)
	{
		cout<<*i<<" ";
	}
	cout<<endl;
}

void fun()
{
	set<int,Compare> s1;  //尖括号里携带的是类型,不能是函数
	s1.insert(2);
	s1.insert(3);
	s1.insert(1);
	s1.insert(4);
	printSet(s1);
}

int main()
{
	fun();
	system("pause");
	return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39

上面程序运行后输出的元素就是按照降序排列的。
使用仿函数改变自定义数据类型排序规则的代码示例如下。

#include <iostream>
#include <string>
#include <set>
using namespace std;

class Person
{
public:
	Person(string name,int age)
	{
		this->name = name;
		this->age = age;
	}
	string name;
	int age;
};

class Compare
{
public:
	bool operator()(const Person &p1,const Person &p2)  //重载(),规定排序类型
	{
		return p1.age > p2.age;  //按年龄降序排列
	}
};

void printSet(set<Person,Compare> &s)
{
	for(set<Person,Compare>::iterator i=s.begin();i!=s.end();i++)
	{
		cout<<"姓名: "<<i->name<<" 年龄: "<<i->age<<endl;
	}
}

void fun()
{
	set<Person,Compare> s1;
	Person p1("A",10);
	Person p2("B",20);
	Person p3("C",30);
	Person p4("D",40);
	s1.insert(p1);
	s1.insert(p2);
	s1.insert(p3);
	s1.insert(p4);
	printSet(s1);
}

int main()
{
	fun();
	system("pause");
	return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54

上面程序运行后的结果如下图所示。
在这里插入图片描述


五、map/multimap容器

map容器中所有的元素都是对组pair型,pair中的第一个元素为key,即键值,第二个元素为value,即实值。map容器中的所有元素都会根据元素的键值自动排序。
map/multimap属于关联式容器,底层的结构是用二叉树实现的,其最大的优点是可以根据键值快速的找到value值。
map容器和multimap容器的区别在于,map容器不允许容器中有重复的键值key,而multimap容器中则允许有重复的键值。

1、构造和赋值

构造包括默认构造和拷贝构造两种,赋值通过=实现。插入操作仍然是使用insert()函数,不过在函数中需要声明类型是pair,然后再传值。
关于map容器构造和赋值的操作示例如下。

#include <iostream>
#include <map>
using namespace std;

void printSet(map<int,int> &m)
{
	for(map<int,int>::iterator i=m.begin();i!=m.end();i++)
	{
		cout<<"key: "<<(*i).first<<" value: "<<(*i).second<<endl;
	}
}

void fun()
{
	map<int,int> m;  //默认构造
	m.insert(pair<int,int>(1,10));
	m.insert(pair<int,int>(4,40));
	m.insert(pair<int,int>(3,30));
	m.insert(pair<int,int>(2,20));
	cout<<"m : "<<endl;
	printSet(m); 
	map<int,int> m1(m);  //拷贝构造
	cout<<"m1 : "<<endl;
	printSet(m1); 
	map<int,int> m2;
	m2 = m;   //赋值操作
	cout<<"m2 : "<<endl;
	printSet(m2); 
}

int main()
{
	fun();
	system("pause");
	return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36

上面程序运行后的结果如下图所示。
在这里插入图片描述

2、大小和交换

关于map容器大小、判空和交换的简单应用如下。

#include <iostream>
#include <map>
using namespace std;

void printSet(map<int,int> &m)
{
	for(map<int,int>::iterator i=m.begin();i!=m.end();i++)
	{
		cout<<"key: "<<(*i).first<<" value: "<<(*i).second<<endl;
	}
}

void fun()
{
	map<int,int> m;  //默认构造
	m.insert(pair<int,int>(1,10));
	m.insert(pair<int,int>(2,20));
	if(!m.empty())
	{
		cout<<"容器非空,大小为: "<<m.size()<<endl;
	}
	else
	{
		cout<<"容器为空!"<<endl;
	}
	cout<<"------交换前------"<<endl;
	cout<<"m : "<<endl;
	printSet(m); 
	map<int,int> m1;
	m1.insert(pair<int,int>(3,30));
	m1.insert(pair<int,int>(4,40));
	cout<<"m1 : "<<endl;
	printSet(m1); 
	cout<<"------交换后------"<<endl;
	m.swap(m1);
	cout<<"m : "<<endl;
	printSet(m); 
	cout<<"m1 : "<<endl;
	printSet(m1); 
}

int main()
{
	fun();
	system("pause");
	return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47

上面程序运行后的结果如下图所示。
在这里插入图片描述

3、插入和删除

map容器插入使用insert()函数,删除使用erase()函数,清空容器使用clear()函数。在使用erase()函数时,既可以传入迭代器的位置,也可以传入要删除的元素,当然也可以传入一个区间进行删除,传入迭代器的首尾位置,其作用和clear()函数一样。插入的方式总共有四种。
关于map容器插入和删除的简单操作示例如下。

#include <iostream>
#include <map>
using namespace std;

void printSet(map<int,int> &m)
{
	for(map<int,int>::iterator i=m.begin();i!=m.end();i++)
	{
		cout<<"key: "<<(*i).first<<" value: "<<(*i).second<<endl;
	}
	cout<<endl;
}

void fun()
{
	map<int,int> m;
	m.insert(pair<int,int>(1,10));  //插入方式一
	m.insert(make_pair(2,20));    //插入方式二
	m.insert(map<int,int>::value_type(3,30));  //插入方式三
	m[4]=40; //插入方式四,不建议使用该方式,因为如果key在容器中不存在会自动创建一个value为0的出来
	cout<<m[5]<<endl;
	printSet(m);
	m.erase(5);  //按照键值key删除
	m.erase(m.begin());  //按照迭代器位置删除
	printSet(m);
	m.clear();  //清空,等价于m.erase(m.begin(),m.end());
	printSet(m);
 }

int main()
{
	fun();
	system("pause");
	return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35

上面程序运行后的结果如下图所示。
在这里插入图片描述

4、查找和统计

map容器中进行查找使用的函数是find(elem),查找元素elem是否存在,其返回值是一个迭代器,如果该元素存在,就返回该元素的迭代器,如果不存在,就返回end()。
map容器中统计元素的个数使用的函数是count(elem),但是其结果只能是0或1,对于multimap容器而言,count(elem)的结果可能大于1。

find(elem);  //返回的是迭代器的位置
count(elem);  //返回元素个数
  • 1
  • 2

关于map容器查找和统计的代码示例如下。

#include <iostream>
#include <map>
using namespace std;

void fun()
{
	map<int,int> m;
	m.insert(pair<int,int>(1,10));
	m.insert(make_pair(2,20));
	m.insert(map<int,int>::value_type(3,30)); 
	map<int,int>::iterator pos = m.find(2);
	if(pos!=m.end())
	{
		cout<<"元素存在! key = "<<(*pos).first<<"  value = "<<(*pos).second<<endl;
		cout<<"键值"<<(*pos).first<<"的个数: "<<m.count((*pos).first)<<endl;
	}
	else
	{
		cout<<"元素不存在!"<<endl;
	}
}

int main()
{
	fun();

	system("pause");
	return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29

上面程序运行后的结果如下图所示。
在这里插入图片描述

5、排序及规则改变

map容器默认排序规则仍然是从小到大,通过自己写仿函数就可以改变排序规则。
使用仿函数改变内置数据排序规则的代码示例如下。

#include <iostream>
#include <map>
using namespace std;

class Compare
{
public:
	bool operator()(int a,int b)
	{
		return a > b;
	}
};

void fun()
{
	map<int,int,Compare> m;
	m.insert(pair<int,int>(1,10));
	m.insert(make_pair(2,20));
	m.insert(map<int,int>::value_type(3,30)); 
	for(map<int,int,Compare>::iterator i=m.begin();i!=m.end();i++)
	{
		cout<<"key = "<<i->first<<" value = "<<i->second<<endl;
	}
}

int main()
{
	fun();

	system("pause");
	return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32

上面程序运行后的结果如下图所示。
在这里插入图片描述
这里需要注意的是,map容器中是对key进行排序的,如果要按照自定义数据的属性进行排序,需要把自定义数据类型放在map容器的第一个位置上。
使用仿函数改变自定义数据排序规则的代码示例如下。

#include <iostream>
#include <string>
#include <map>
using namespace std;

class Person
{
public:
	Person(string name,int age)
	{
		this->name = name;
		this->age = age;
	}
	string name;
	int age;
};

class Compare
{
public:
	bool operator()(Person p1,Person p2)
	{
		return p1.age > p2.age;
	}
};

void fun()
{
	map<Person,int,Compare> m;
	Person p1("Tom",10);
	Person p2("Jack",30);
	Person p3("Hole",20);
	m.insert(pair<Person,int>(p1,1));
	m.insert(pair<Person,int>(p2,2));
	m.insert(pair<Person,int>(p3,3));
	for(map<Person,int,Compare>::iterator i=m.begin();i!=m.end();i++)
	{
		cout<<"key = "<<i->second<<" name = "<<i->first.name<<" age = "<<i->first.age<<endl;
	}
}

int main()
{
	fun();

	system("pause");
	return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48

上面程序运行后的结果如下图所示。
在这里插入图片描述
从输出结果可以看出,排序是按照自定义数据的年龄属性降序排列的。


六、员工分组案例

公司招聘了10个员工ABCDEFGHIJ,现在需要指派员工在那个部门工作,员工信息包括姓名和工资,部门分为策划、美术、研发。随机给10名员工分配部门和工资,通过multimap容器进行信息的插入,key表示部门编号,value表示员工,可以分部门显示员工信息。
实现步骤:创建10名员工,放到vector中;遍历vector容器,取出每个员工,进行随机分组;分组后,将员工部门编号作为key,具体员工作为value,放入到multimap容器中;分部门显示员工信息。
该案例的代码实现如下。

#include <iostream>
#include <string>
#include <vector>
#include <map>
#include <ctime>
using namespace std;

class Staff
{
public:
	Staff(string name,int wage)
	{
		this->name = name;
		this->wage = wage;
	}
	string name;
	int wage;
};

void createStaff(vector<Staff> &v)
{
	string nameSeed = "ABCDEFGHIJ";
	for(int i=0;i<10;i++)
	{
		string name = "员工";
		name += nameSeed[i];
		int wage = rand()%1000+3000;  //3000-3999
		Staff s(name,wage);
		v.push_back(s);
	}
}

void setGroup(vector<Staff> &v,multimap<int,Staff> &m)
{
	for(vector<Staff>::iterator i=v.begin();i!=v.end();i++)
	{
		int dep_num = rand()%3+1;   //1-3  1策划、2美术、3研发
		m.insert(make_pair(dep_num,*i));   //给员工分配部门
	}
}

void showInfo(multimap<int,Staff> &m)
{
	cout<<"--------策划部门--------"<<endl;
	multimap<int,Staff>::iterator pos = m.find(1);  //找到起始位置
	for(int i=0;i<m.count(1) && pos!=m.end();i++)  //数量
	{
		cout<<"姓名: "<<pos->second.name<<"  工资:"<<pos->second.wage<<endl;
		++pos;
	}
	cout<<"--------美术部门--------"<<endl;
	pos = m.find(2);
	for(int i=0;i<m.count(2) && pos!=m.end();i++)
	{
		cout<<"姓名: "<<pos->second.name<<"  工资:"<<pos->second.wage<<endl;
		++pos;
	}
	cout<<"--------研发部门--------"<<endl;
	pos = m.find(3);
	for(int i=0;i<m.count(3) && pos!=m.end();i++)
	{
		cout<<"姓名: "<<pos->second.name<<"  工资:"<<pos->second.wage<<endl;
		++pos;
	}
}

void printStaff(vector<Staff> &v)
{
	for(vector<Staff>::iterator i=v.begin();i!=v.end();i++)
	{
		cout<<"name : "<<i->name<<"  wage : "<<i->wage<<endl;
	}
}

int main()
{
	srand((unsigned int)time(NULL));  //加入随机数种子,保证生成数的随机性
	vector<Staff> v;  //创建vector容器
	createStaff(v);  //创建员工信息
	printStaff(v);
	multimap<int,Staff> m;  //创建multimap容器
	setGroup(v,m);  //员工分组
	showInfo(m);   //分组显示员工信息
	system("pause");
	return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86

案例的运行结果如下图所示。
在这里插入图片描述


本文参考视频:
黑马程序员匠心之作|C++教程从0到1入门编程,学习编程不再难

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/Li_阴宅/article/detail/1001747
推荐阅读
相关标签
  

闽ICP备14008679号