实验4 STL应用
实验内容
- 撰写自己的算法和函数,结合容器和迭代器解决序列变换(如取反、平方、立方),像素变换(二值化、灰度拉伸);
- 用set存储学生信息,并进行增删改查操作;
- 输入一个字符串,用map统计每个字符出现的次数并输出字符及对应的次数。
初识STL
STL的诞生
- 长久以来,软件界一直希望建立一种可重复利用的东西
- C++的面向对象和泛型编程思想,目的就是复用性的提升
- 大多情况下,数据结构和算法都未能有一套标准,导致被迫从事大量重复工作
- 为了建立数据结构和算法的一套标准,诞生了STL
STL的六大组件
STL大体分为六大组件,分别是:容器、算法、迭代器、仿函数、适配器(配接器)、空间配置器
1.容器:各种数据结构,如vector、list、 deque、set、map等,用来存放数据。
2.算法:各种常用的算法,如sort、find、copy、for_each等
3.迭代器:扮演了容器与算法之间的胶合剂。
4.仿函数:行为类似函数,可作为算法的某种策略。
5.适配器:一种用来修饰容器或者仿函数或迭代器接口的东西。
6.空间配置器:负责空间的配置与管理。
容器
容器:置物之所也
STL容器就是将运用最广泛的一些数据结构实现出来
常用的数据结构:数组,链表,树,栈,队列,集合,映射表等
这些容器分为序列式容器和关联式容器两种:
序列式容器:强调值的排序,序列式容器中的每个元素均有固定的位置。关联式容器:二叉树结构,各元素之间没有严格的物理上的顺序关系
算法
算法:问题之解法也
有限的步骤,解决逻辑或数学上的问题,这一门学科我们叫做算法(Algorithms)
算法分为:质变算法和非质变算法。
质变算法:是指运算过程中会更改区间内的元素的内容。例如拷贝,替换,删除等等
非质变算法:是指运算过程中不会更改区间内的元素内容,例如查找、计数、遍历、寻找极值等等
迭代器
迭代器:容器和算法之间粘合剂
提供一种方法,使之能够依序寻访某个容器所含的各个元素,而又无需暴露该容器的内部表示方式。每个容器都有自己专属的迭代器
迭代器使用非常类似于指针,初学阶段我们可以先理解迭代器为指针
常用的容器中迭代器种类为双向迭代器,和随机访问迭代器
vector存放内置数据类型
STL中最常用的容器为vector,可以理解为数组
容器:vector
算法:for_each
迭代器:vector<int>::iterator
//创建一个vector容器
vector<int> v;
//向容器中插入数据
v.push_back(10);
v.push_back(20);
v.push_back(30);
v.push_back(40);
//通过迭代器访问容器中的数据
vector<int>::iterator itBegin = v.begin()//起始迭代器,指向容器中第一个元素
vector<int>::iterator itEnd = v.end()//结束迭代器,指向容器中最后一个元素的下一个位置
//第一种遍历方式
while(itBegin != itEnd)
{
cout<<*itBegin<<endl;
itBegin++;
}
//第二种遍历方式
for(vector<int>::iterator it = v.begin();it != v.end();it++)
{
cout<<*it<<endl;
}
//第三种遍历方式
#include <algorithm>//标准算法头文件
void MyPrint(int val)
{
cout<<val<<endl;
}
for_each(v.begin(),v.end(),MyPrint);
vector存放自定义数据类型
class Person
{
public:
Person(string name, int age)
{
this->m_name = name;
this->m_age = age;
}
string m_name;
int m_age;
}
void test01()
{
vector<Person>v;
Person p1("a",10);
Person p2("b",20);
Person p3("c",30);
Person p4("d",40);
//向容器中添加数据
v.push_back(p1);
v.push_back(p2);
v.push_back(p3);
v.push_back(p4);
//遍历容器中的数据
for(vector<Person>::iterator it;it!=v.end();it++)
{
//方法一:
cout<<(*it).m_name;
cout<<(*it).m_age<<endl;
//方法二:
cout<<it->m_name;
cout<<it->m_age<<endl;
}
}
vector容器嵌套容器
void test01()
{
vector<vector<int>>v;
//创建小容器
vector<int>v1;
vector<int>v2;
vector<int>v3;
vector<int>v4;
//往小容器红插入数据
for(int i=0;i<4;i++)
{
v1.push_back(i+1);
v1.push_back(i+2);
v1.push_back(i+3);
v1.push_back(i+4);
}
//将小容器插入到大容器
v.push_back(v1);
v.push_back(v2);
v.push_back(v3);
v.push_back(v4);
//遍历
for(vector<vector<int>>::interator it=v.begin();it!=v.end();it++)
{
for(vector<int>::iterator vit=(*it).begin();vit!=(*it).end();vit++)
{
cout<<*vit<<endl;
}
cout<<endl;
}
}
撰写自己的算法和函数,结合容器和迭代器解决序列变换,像素变换
序列变换
//取反
template<typename T>
T InvT(T a)
{
return -a;
}
//平方
T SqrT(T a)
{
return a*a;
}
//立方
T CubeT(T a)
{
return a*a*a;
}
//自定义操作运算函数
template<typename inputIter, typename outputIter, typename MyOperator>
void transCalcT(inputIter begInput, inputIter endInput, outputIter begOutput, MyOperator op)
{
for(;begInput != endInput;begInput++, begOutput++)
{
*begOutput = op(*begInput);
}
}
//测试函数
void test01()
{
const int N = 5;
int a[N] = {1,2,3,4,5};
int b[N];
//设置一个容量为5的容器vb,数据类型为double
vector<double> vb(N);
transCalcT(a,a+N,vb.begin(),InvT<int>);
}
像素变换
// 二值化
template<typename T>
class MyThreshold{
public:
MyThreshold(int n=128):_nThreshold(n){}
int operator()(T val)
{
//在二值化过程中,对每一个像元进行二值化处理,若小于2,则置为0,反之则置为1
return val<_nThreshold?0:1;
}
int _nThreshold;
};
//灰度变换
template <typename T>
class Mytrans
{
public:
Mytrans(int n=128):c(n){}
int operator()(T val)
{
//将每一个像元大小增加10
return val+=10;
}
int c;
};
set
基本概念:
所有元素都会在插入时自动被排序
本质:
set/multiset属于关联式容器,底层结构是用二叉树实现。
set和multiset区别:
set不允许容器中有重复的元素;multiset允许容器中有重复的元素
用set存储学生信息,并进行增删改查操作
//用set存储学生信息,并进行增删改查操作
//创建一个Student类
class Student
{
public:
Student(string name, int age)
{
//初始化赋值
this->m_name = name;
this->m_age = age;
}
string m_name;
int age;
}
class compareStudent
{
public:
bool operator()(const Student&s1, const Student&s2)
{
//按照年龄降序
return p1.m_age > p2.m_age;
}
// 根据姓名删除元素
bool del(string Name){
for(auto it:s){
if(!Name.compare(it.m_name)){
s.erase(it);
return true;
}
}
return true;
}
// 根据姓名查找年龄
int searchAge(string Name){
for(auto it:s){
if(!Name.compare(it.m_name)){
return it.m_age;
}
}
return -1;
}
// 根据姓名修改年龄
void updateInfo(string name, int afterAge){
for(auto it:s){
if(it.m_name==name){
s.erase(it);
break;
}
}
s.insert(Student(name, afterAge));
}
//测试函数
void test01()
{
//自定义数据类型,都会制定排序规则
set<Student,compareStudent>s;
//创建学生信息
Student s1("刘备",20);
Student s2("关羽",25);
Student s3("张飞",36);
Student s4("赵云",41);
//插入set容器中
s.insert(s1);
s.insert(s2);
s.insert(s3);
s.insert(s4);
//遍历s中的学生信息
for(set<Student,compareStudent>::iterator it = s.begin();it!=s.end();it++)
{
cout<<"姓名: "<<it->m_name<<" 年龄: "<<it->m_age<<endl;
}
}
map
map的基本概念:
map中所有元素都是pair
pair中第一个元素为key(键值),起到索引作用,第二个元素为value(实值)
所有元素都会根据元素的键值自动排序
本质:
map/multimap属于关联式容器,底层结构是用二叉树实现。
优点:
可以根据key值快速找到value值
map和multimap区别:
map不允许容器中有重复key值元素
multimap允许容器中有重复key值元素
map默认构造函数:map<T1, T2> mp
map拷贝构造函数:map(const map &mp)
赋值 重载等号操作符:map& operator=(const map &mp)
输入一个字符串,用map统计每个字符出现的次数并输出字符及对应的次数
void countChar(string str){
map<char, int> countChar;
//遍历字符串中的每一个字符
for(int i = 0;i<str.length();i++){
// 如果第一次出现就赋值为1,直到遍历完这个map
if(countChar.find(str[i])==countChar.end()){
countChar[str[i]] = 1;
}
// 若不是第一次出现,则该字符++
else{countChar[str[i]]++;}
}
// 打印每个单词出现的次数
for(auto i:countChar){
countChar<<i.first<<": "<<i.second<<" ";
}
}
自写案例
需要完成的内容:
- 公司今天招聘了10个员工(ABCDEFGHI),10名员工进入公司之后,需要指派员工在哪个部门工作
- 员工信息有:姓名 工资组成,部门分为、策划、美术、研发
- 随机给10名员工分配部门和工资
- 通过multimap进行信息的插入key(部门编号)value(员工)
- 分部门显示员工信息
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;//三个部门
//将员工插到分组中,key部门编号,value具体员工
m.insert(make_pair(deptID, *it);
}
}
//分组显示员工
void showWorkerByGroup(multimap<int, Worker>&m)
{
cout<<"策划部门; "<<endl;
multimap<int, Worker>::iterator pos = m.find(0);
int count = m.count(0);
int index = 0;
for(;pos!=m.end() && index<count;pos++,index++)
{
cout<<"姓名: "<<pos->second.m_name<<" 工资: "<<pos->second.m_salary<<endl;
}
cout<<"---------------------------------------"<<endl;
cout<<"美术部门; "<<endl;
pos = m.find(0);
count = m.count(0);
index = 0;
for(;pos!=m.end() && index<count;pos++,index++)
{
cout<<"姓名: "<<pos->second.m_name<<" 工资: "<<pos->second.m_salary<<endl;
}
cout<<"---------------------------------------"<<endl;
cout<<"研发部门; "<<endl;
pos = m.find(0);
count = m.count(0);
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);
//2、员工分组
multimap<int, Worker>mWorker;
setGroup(vWorker, mWorker);
//3、分组显示员工
showWorkerByGroup(mWorker);
system("pause");
return 0;
}