1.迭代器简介
什么是迭代器
- 是一种检查容器内元素并遍历元素的数据类型
迭代器的主要功能
- 其主要功能是来访问标准库中容器的元素
- 字符串可以使用迭代器,可以近似把字符串看作是字符的容器
- 迭代器有有效和无效之分
2. 使用迭代器
begin和end
- begin表示获得指向第一个元素的迭代器
- svec不包含任何元素,空vector
- end表示获得最后一个元素的下一个位置的迭代器
- 返回一个根本不存在的尾部元素
- 通常称为尾后迭代器,或者尾迭代器
- 迭代器可以使用auto来定义begin和end变量
- svec不包含任何元素,空vector
- 返回一个根本不存在的尾部元素
- 通常称为尾后迭代器,或者尾迭代器
迭代器运算
- 获得指向的元素本身
- 通过使用解引用迭代器来获取,与指针类似 (*item)
- 注意不要尝试使用解引用来获得尾迭代器
- 使用的时候要判断容器是否为空,这个与索引使用类似
迭代器的移动
- 使用++或者--来一定迭代器
- 在移动的过程中要注意其范围,判断其是否到了end就无法移动了
迭代器的类型
- 总体上就两个大类
- 非常量迭代器 vector<int>::iterator it
- 可读可写
- 如果容器的对象时非常量是可以使用iterator 或者 const_iterator
- 常量迭代器 vector<int>::const_iterator it
- 只能读不能写
- 如果容器的对象时常量是只能使用 const_iterator
- 非常量迭代器 vector<int>::iterator it
- cbegin 和 cend
- 可以同时使用在上面的两种迭代器,只是最终的类型不同而已
- vector<int> v ; auto it1 = v.begin();
- const vector<int> cv; auto it2 = cv.begin();
- cbegin 就表示只是使用了 const_iterator;
- vector<int> v ; auto it1 = v.cbegin();
- 可以同时使用在上面的两种迭代器,只是最终的类型不同而已
解引用来访问具体元素
- 解引用的格式 (*it)以及成员访问
- (*it).empty()
- *it.empty()
- 以上两个是本质的区别的
- 成员访问简化(->)
- (*it).empty()
- it->empty()
- 以上两个是一样的,以后要经常使用后者
迭代器失效
- 不能在范围for循环中向vector对象添加元素
- 任何一种可能改变vector对象容器的操作,都会是该vector对象的迭代器失效
实例练习:
- 尝试获得尾迭代器的值
- 利用迭代器修改第一个字符为大写
- 利用迭代器修改第一个词为大写
- 对比 (*it).empty() 和 *it.empty()
- 迭代器类型的实例
#pragma region begin 和 end迭代器
//迭代器begin和end是两个关键的位置元素
//尤其end并不是最后一个元素,而是最后一个元素后面的一个位置,并不是元素
vector<int> ivec{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
auto ibegin = ivec.begin();
auto iend = ivec.end();
cout << "begin =" << *ibegin << endl;
//cout << "end =" << (int)*iend << endl; //会报错,因为最后一个没有任何东西
#pragma endregion
#pragma region 访问并修改string为大写
//这里尤其要注意如何使用迭代器来访问和修改元素
string s = "my name is wall-e";
auto sbegin = s.begin();
auto send = s.end();
if (isalpha(*sbegin) && sbegin != send)
{
*sbegin = toupper(*sbegin);
}
cout << s << endl;
#pragma endregion
#pragma region 访问并修改string第一个词和全部为大写
string s1 = "my name is wall-e";
string::iterator it = s1.begin();
string::iterator send1 = s1.end();
while (isalpha(*it) && it != send1 && !isspace(*it))
{
*it = toupper(*it);
++it;
}
cout << s1 << endl;
while (it != send1)
{
*it = toupper(*it);
++it;
}
cout << s1 << endl;
#pragma endregion
#pragma region 常量迭代器类型的实例
//if (ivec > svec); //err 无法比较两个不同的类型
vector<int> ivec2{ 1, 2, 3, 4, 5, 6 };
const vector<int> ivec3{ 1, 2, 3, 4, 5, 6 };
//ivec3.push_back(7); //无法添加新元素
//*ivec3.begin() = 7; //无法修改其元素的值
//只能使用const_iterator来定义
vector<int>::const_iterator constBegin = ivec3.begin();
//vector<int>::iterator testBegin = ivec3.begin(); //无法使用普通迭代器来获取
//对于普通的vector容器是可以使用常量和非常量迭代来获取
vector<int>::const_iterator constbegin = ivec2.begin();
vector<int>::iterator testBegin = ivec2.begin();
#pragma endregion
3.迭代器运算(vector, string)
- 所有的标准容器包括vector,string等
- 支持++,--移动运算
- 也可以使用==、!=进行比较
- vector,string还包括
- 每一移动跨过多个元素
- 迭代器之间关系运算
迭代器算术运算
- 计算中间位置的迭代器
- auto min = vi.begin() + vi.size() / 2;
- 判断两个迭代器前后位置<, >,>=,<=
- 两个迭代器最差运算 it1 - it2
- 返回值为difference_type类型,其为带符号的整数
利用迭代器实现二分搜索算法
- 二分法是在一个有序的序列中使用的
- 算法步骤
- 先从中间位置搜索,如果中间位置是要找的值则停止
- 中间位置不符合,则比较他们之间的大小
- 如果中间位置大于要找的值,则在前半部分查找,
- 如果中间位置小于要找的值,则在后半部分查找,
实例练习:
- 二分法算法
// 查询11的位置
vector< int> ivecS{ 1, 3, 5, 7, 9, 11,13 };
auto beginS = ivecS.begin();
auto midS = beginS + ivecS.size() / 2;
auto endS = ivecS.end();
while (*midS != 11)
{
if (*midS > 11)
{
beginS = midS+1;
}
else
{
endS = midS;
}
midS = beginS + (endS - beginS) / 2;
}