第1关:动态学生信息管理
相关知识
STL 的代码从广义上讲分为三类:algorithm(算法)、container(容器)和iterator(迭代器),几乎所有的代码都采用了模板类和模版函数的方式,这相比于传统的由函数和类组成的库来说提供了更好的代码重用机会。
本实训我们就一起来学习 vector 容器、sort 和 find 算法以及迭代器的使用。
vector 容器
vector 是顺序容器的一种。vector 是可变长的动态数组,支持随机访问迭代器,所有 STL 算法都能对 vector 进行操作。要使用 vector,需要包含头文件 vector。
vector 容器在实现时,动态分配的存储空间一般都大于存放元素所需的空间。例如,哪怕容器中只有一个元素,也会分配 32 个元素的存储空间。这样做的好处是,在尾部添加一个新元素时不必重新分配空间,直接将新元素写入适当位置即可。
它的常用构造函数如下:
int main()
{
vector<string> v; // 无参数版
vector<string> v2(10); // 初始容量为 10 的 vector,每个元素为都为 string 的默认值
vector<string> v3(10,"abc"); // 初始容量为 10,且每一个元素都为 abc
}
如果要访问或者修改 vector 的内容,可以用如下的函数:
vector<int> v;
v.push_back(10); // 将一个元素添加到容器的末尾
v.push_back(20);
cout << v[0] << endl; // 访问索引为 0 的元素,即 10
v.erase(v.begin() + 1); // 删除索引为 1 的元素,begin() 函数的含义见下文
v.insert(v.begin() + 1,30); // 在索引为 1 的元素之前插入一个元素
v.clear(); // 删除所有元素
同时,vector 还有如下的成员函数用于查询元素数量:
int size = v.size(); // 返回元素数量
bool isem = v.empty(); // 容器为空则返回 true
vector 的成员函数不仅仅只有这些,感兴趣的同学可以查阅其他资料进行学习。
迭代器
STL 中的容器都提供了迭代器( iterator )成员类型,它就像一个指针,使用它能按照一种特定的顺序遍历容器中的所有元素。
一般来说,当你对一个迭代器 it 进行it++自增操作时,就能进入到下一个元素,进行it–自减操作时,就能回到上一个元素。有些迭代器还支持it+=2,it-=2这样跨多个元素的操作。并对它使用*it取值操作,就能获得当前的元素值。
上文提到的begin()函数,返回的其实就是一个指向第一个元素的迭代器,与之对应的还有一个end()函数,它返回一个指向最后一个元素之后不存在的元素的迭代器,有了这两个函数,我们就能访问 vector 中的所有元素了,代码如下:
vector<int> v(5,10); // 容量为 5,元素值都是 10
for(vector<int>::iterator it = v.begin();it != v.end();it++)
cout << *it << " ";
输出结果为:10 10 10 10 10
此时我们也可以知道,vector 的 erase ,insert 函数的参数实际上都是迭代器类型。
注意:在使用迭代器迭代一个容器的过程中,使用修改容器内容的函数可能会使得迭代器无效,因此尽量不要在这个过程中修改容器内容。
sort 算法
在很多应用中,排序都是至关重要的,而且很多 STL 算法也只适用于有序对象序列。
sort 算法位于头文件,它接收两个迭代器参数 first 和 last,代表要排序的范围,为左闭右开,即[first,last),然后将这个范围里的元素按照升序排列,并要求迭代器的元素类型重载了此类型对象间用于比较的<运算符,即表现形式为:
sort(first,last);
例如:
vector<int> v;
v.push_back(2);
v.push_back(1);
int vs[]={2,1};
sort(v.begin(),v.end()); // 对 vector 排序
sort(vs,vs + 2); // 对数组排序
排序后两个容器内的元素都是:1 2
注意:指针也可以看做是一种迭代器,所以对于数组也是可以使用这个函数的。
find 算法
find 算法位于头文件,它接收三个参数,前两个迭代器类型的 first ,last 参数代表范围,左闭右开,第三个 value 参数代表要查找的内容,因此元素类型必须重载用于元素间比较 value 值的==运算符,即表现形式为:
find(first,last,value);
它返回的也是一个迭代器,如果找到了这个元素,则返回指向这个元素的迭代器,如果没有,则返回 last 参数值。
例如:
vector<int> v;
for (size_t i = 0; i < 10; ++i)
{
v.push_back(i+1);
}
v.push_back(10);
vector<int>::iterator iLocation = find(v.begin(), v.end(), 10); // find 操作查找等于 10 的元素,并返回指向该元素的迭代器,如果没有找到,返回指向集合最后一个元素的迭代器。
if (iLocation != v.end())
{
cout << "找到元素 10" << endl;
}
cout << "前一个元素为:" << *(--iLocation) << endl;
输出结果为:
找到元素 10
前一个元素为:9
注意:find 函数在比对元素的时候,==运算符的第二个参数是要找的内容,即参数 value。
编程 动态学生信息管理
在右侧编辑器中的Begin-End之间补充代码,设计一个学员信息表,并在主函数中读取输入,根据输入的内容作出相应的动作,输入内容共有4种格式,具体如下:
A 姓名 分数:向信息表的末尾添加一条记录,内容为姓名 分数,分数均为整数。如果表中已有同一个人的记录,那就只更新分数。
R 姓名:删除表中指定姓名的条目,不存在则不做处理。
P,按照姓名 分数 的格式打印整个信息表,每条记录占1行。如果表为空,也要输出空行。
S,将整个信息表的数据按照分数的降序排序。
注意:为了保证排序结果稳定,输入的数据保证不会有两条记录有相同的分数。
#include <iostream>
#include <vector>
#include <string>
#include <algorithm>
using namespace std;
/********* Begin *********/
//自定义的类和者其他内容
struct Student
{
string name_;
int score_;
};
bool cmp(Student st1, Student st2)
{
return st1.score_ > st2.score_;
}
/********* End *********/
int main()
{
/********* Begin *********/
char ch;
string name;
int score;
vector<Student> st;
//读取输入数据,执行动作
while (cin >> ch)
{
switch (ch)
{
case 'A':
{
cin >> name >> score;
bool flag = true;
for (int i = 0; i < st.size(); i++)
{
if (st[i].name_ == name)
{
st[i].score_ = score;
flag = false;
}
}
Student t;
t.name_ = name;
t.score_ = score;
if (flag) st.push_back(t);
break;
}
case 'R':
{
cin >> name;
for (int i = 0; i < st.size(); i++)
if (st[i].name_ == name)
{
st.erase(st.begin() + i);
break;
}
break;
}
case 'P':
{
for (int i = 0; i < st.size(); i++)
{
cout << st[i].name_ << " " << st[i].score_ << endl;
}
if (st.size() == 0) cout << "[空]" <<endl;
break;
}
case 'S':
{
sort(st.begin(), st.end(), cmp);
break;
}
default: break;
}
}
return 0;
/********* End *********/
}
第2关:还原键盘输入
相关知识
为了完成本关任务,你需要掌握 list 容器的使用。
list 容器
list 位于头文件,是 STL 提供的一种顺序存储的容器,它不支持通过位置索引访问,但能高效的进行插入、删除元素操作,通常由双向链表实现。
图1 - list 采用的双向循环链表结构
list对象的创建,初始化赋值方法与vector相同,它常用的构造函数如下:
list<int> l(); // 无参构造函数
list<int> l2(10); // 包含 10 个默认值 0 元素
list<int> l3(10,123); // 包含 10 个 123 元素
由于链表中的数据需要一个个元素进行遍历,因此,list 元素的遍历使用迭代器的方式进行,通过它的 begin ,end 函数用于获取指向第一个元素的迭代器和指向最后一个元素之后的迭代器。例如:
int main()
{
//将数据插入链表
list<int> li(5,123);
list<int>::iterator i = li.begin(),j=li.end();
//遍历打印链表元素
for(i ; i != j ; i++ )
{
cout << (*i) << endl;
}
return 0;
}
输出结果为:
123
123
123
123
123
如果要修改容器内容,可以使用以下函数:
list<int> l;
l.push_back(1); // 添加到链尾
l.push_front(2); // 添加到链头
l.pop_back(); // 删除链尾元素
l.pop_front(); // 删除链头元素
l.insert(l.begin(),3); // 在指定迭代器之前添加一个元素
l.erase(l.begin()); // 删除指定迭代器所指的元素
list 的迭代器有一点特别,当对容器进行修改时,只要不是删除这个迭代器元对应的元素,这个迭代器都不会失效。
例如:
list<int> l;
list<int>::iterator it = l.begin();
l.insert(it,10); // 执行之后 it 依然有效,并且现在 it 不再与l.begin() 相等
l.erase(it-1); // 执行之后 it 依然有效
l.erase(it); // 现在 it 无效了
如果要查询 list 的元素数量,可以使用以下函数:
int size = l.size(); // 返回元素个数
bool isem = l.empty(); // 容器为空时返回 true
list 的成员函数不仅仅只有这些,感兴趣的同学可以查阅其他资料进行学习。
编程 还原键盘输入
在右侧编辑器中的Begin-End之间补充代码,读取输入的键盘操作记录,并根据操作记录还原最后的输入结果。每一次的操作记录为一行字符串,字符串的内容分为以下几类:
大小写字母,数字:代表用户在输入光标的左侧输入了一个具体字符,光标保持在新输入字符的右侧。
<,>字符:代表用户按下了左键或者右键,作用是输入光标向左或者向右移动一个位置。
[,]字符:代表用户按下了 Home 键或者 End 键,作用是移动输入光标到最开始或者最末尾的位置。
注意:每一次操作记录开始的时候都是没有输入的且输入光标只能在已输入内容的两端和内部移动。
#include <iostream>
#include <string>
#include <list>
using namespace std;
int main()
{
/********* Begin *********/
//读取输入,解析并输出复原后的输出
char ch;
list<char> li;
list<char>::iterator it = li.begin();
while((ch = getchar()) != EOF)
{
switch(ch)
{
case '\n':
{
for(list<char>::iterator i = li.begin();i != li.end();i++)
{
cout << *i;
}
cout << endl;
li.clear();
it = li.begin();
break;
}
case '<':
{
it--;
break;
}
case '>':
{
it++;
break;
}
case '[':
{
it = li.begin();
break;
}
case ']':
{
it = li.end();
break;
}
default:
{
li.insert(it, ch);
break;
}
}
}
for(list<char>::iterator i = li.begin(); i != li.end(); i++)
cout << *i;
cout<<endl;
/********* End *********/
}
转载:https://blog.csdn.net/qq_30170949/article/details/119090431