杂记1:正向反向迭代器,不同stl容器的lower_bound的使用(set,map,vector,arr,pair),数字转字符串

感觉自己记性真的不行,很多之前用过,查过的知识点都忘掉了,开个博客记一下吧。

迭代器

基本格式: 容器类名::iterator  迭代器名;

举两个例子
vector<int>::iterator ite;
map<int,int>::iterator ite;

迭代器又分四种,常用的是正向迭代器(上面那个)和反向迭代器,正弦迭代器指向容器头从前往后遍历,反向迭代器指向容器尾从后往前遍历。

迭代器类型定义
正向迭代器容器类名::iterator 迭代器名;
常量正向迭代器容器类名::const_iterator 迭代器名;
反向迭代器容器类名::reverse_iterator 迭代器名;
常量反向迭代器容器类名::const_reverse_iterator 迭代器名;


浅测一下正向和反向

#include<iostream>
#include<vector>
using namespace std;
int main() {
	vector<int>v;
	v.push_back(1);
	v.push_back(2);
	v.push_back(3);

    //正向迭代器
	vector<int>::iterator itez = v.begin();
	while (itez != v.end()) {
		cout << *itez << endl;//不要漏掉*
		itez++;
	}
	//1
	//2
	//3
	cout << endl;
    
    //反向迭代器
	vector<int>::reverse_iterator itef = v.rbegin();
	while (itef != v.rend()) {
		cout << *itef << endl;
		itef++;
	}
	//3
	//2
	//1
	return 0;
}

这边注意一下:

  1. 容器.end()指向的是容器中最后一个元素的下一个,所有不要尝试让正向迭代器指向容器.end(),通过这样配合迭代器–逆向遍历,这是不可行的,哒咩!!!
    同理.容器.rend()指向的是容器第一个元素的上一个位置!!

  2. 迭代器其实和指针很像,指向的是一片地址空间,不是存储的内容(值)。通过迭代器可以读取它指向的元素,*迭代器名就表示迭代器指向的元素。通过非常量迭代器还能修改其指向的元素。常量迭代器不允许修改指向的元素。

  3. 迭代器都可以进行++操作。反向迭代器和正向迭代器的区别在于:对正向迭代器进行++操作时,迭代器会指向容器中的后一个元素(正序遍历);而对反向迭代器进行++操作时,迭代器会指向容器中的前一个元素。(逆序遍历)

  4. 一个不被注意的知识:写++ite相比于写ite++,程序的执行速度更快。因为后置++要多生成一个局部对象 tmp,因此执行速度比前置的慢。

  5. 容器适配器 stack、queue 和 priority_queue 没有迭代器。

不同容器的迭代器的功能

容器迭代器功能
vector随机访问
deque随机访问
list双向
set / multiset双向
map / multimap双向
stack不支持迭代器
queue不支持迭代器
priority_queue不支持迭代器
  • 正向迭代器。支持以下操作:++ite,ite++,*ite。此外,两个正向迭代器可以互相赋值,还可以用==和!=运算符进行比较。

  • 双向迭代器。除++ite,ite++,*ite之外,还可以–ite,ite–。–ite使得 ite朝和++ite相反的方向移动。

  • 随机访问迭代器。正向反向都行。而且可以随机访问。
    p+=i,p-=i,p+i,p-i还有p[i]:返回 p 后面第 i 个元素的引用,比如p指向容器vector<int>v的开头,那么p[i]就是v数组的第i个元素。



lower_bound,upper_bound

//在 [first, last) 区域内查找不小于/大于等于 val 的元素
ForwardIterator lower_bound (ForwardIterator first, ForwardIterator last,const T& val);
//查找[first, last)区域中第一个大于 val 的元素。
ForwardIterator upper_bound (ForwardIterator first, ForwardIterator last,const T& val);

二分查找
lower_bound找的是第一个比val大于等于的数
upper_bound找的是第一个比val大于的数
查找结束,函数返回一个正向迭代器,当查找成功时,迭代器指向找到的元素;反之,如果查找失败,迭代器的指向和 指向容器end()迭代器相同。
如果要返回元素的位置,记得要将查找结果与容器.begin()相减。

int divG = lower_bound(onlyGrades.begin(), onlyGrades.end(), G)-onlyGrades.begin();

其实还有equal_range() 以及 binary_search(),但我好像没用过。
这四个函数底层实现采用的都是二分查找的方式。


这里补充一下我之前没注意过的关于lower_bound的一个知识点,upper_bound同理:
上面那种语法格式其实也有设定有比较规则,只不过此规则无法改变,即使用 < 小于号比较 [first, last) 区域内某些元素和 val 的大小(这块可以理解为比较范围内的元素从小到大排序),直至找到一个不小于 val 的元素。这也意味着,如果使用第一种语法格式,则 [first,last) 范围的元素类型必须支持 < 运算符。

其实我们手写二分查找也是要求元素顺序排序。
第一种语法规则要求数据必须从小到大排列

可以忽略,我暂时没发现用处

然后我们看一下第二种语法规则,第二种语法规则可以使数据内元素不受从小到大的排序限定。

//在 [first, last) 区域内查找第一个不符合 comp 规则的元素
ForwardIterator lower_bound (ForwardIterator first, ForwardIterator last,const T& val, Compare comp);
//查找[first, last)区域中第一个不符合 comp 规则的元素
ForwardIterator upper_bound (ForwardIterator first, ForwardIterator last,const T& val, Compare comp);

其中,first 和 last 都为正向迭代器,[first, last) 用于指定函数的作用范围;val 用于指定目标元素;comp 用于自定义比较规则,comp作为参数可以接收一个包含 2 个形参(第二个形参值始终为 val)且返回值为 bool 类型的函数,可以是普通函数,也可以是函数对象。

举个例子

#include <iostream>     
#include <algorithm>    
#include <vector>       
using namespace std;
class mycomp {
public:
    bool operator()(const int& i, const int& j) {
        return i > j;
    }
};

int main() {
    vector<int> myvector{ 4,5,3,1,2 };
    //根据 mycomp2 规则,从 myvector 容器中找到第一个违背 mycomp 规则的元素
    vector<int>::iterator iter = lower_bound(myvector.begin(), myvector.end(), 3, mycomp());
    cout << "*iter = " << *iter;
    return 0;
}

这段代码中,val=3作为mycomp的第二个参数j传入,也就是说这段代码的作用就是找到没有排序规律的容器myvector中第一个不满足元素大于3(j)的元素的位置。
如果myvector{ 4,5,3,1,2 }变成myvector{ 1,5,3,1,2 }结果还是3,因为这玩意的基本实现是二分查找!!咱也不懂这玩意有啥用,我本来是想找实现反向二分查找的…

这个比较有用:不同容器的lower_bound

前提,元素从小到大排序

1.vector,数组
int pos=lower_bound(Vector.begin(),Vector.end(),val)-Vector.begin();
int pos=lower_bound(Arr,Arr+数组的大小,val)-Arr;

2.set,map(本身是按照key从小到大排序)
set<int>::iterator it;
it=Set.lower_bound(val);
map<intint>::iterator it;
it=Map.lower_bound(val);


暂时就知道这两种

这边再补充一下pair作为vector的元素类型时候如何使用lower_bound:
前置知识:
1.pair主要的作用是将两个数据组合成一个数据,两个数据可以是同一类型或者不同类型。实质上就是一个结构体。初始化一个pair可以使用构造函数,也可以使用std::make_pair函数。

make_pair{first_ele,second_ele}

pair+lower_bound

2.pair作为vector的数据类型进行sort排序,默认排序是先比较first再比较second。

按照前置知识2看来,lowwer_bound和pair的联合使用可能使先判断first元素再判断second元素

#include <iostream>     
#include <algorithm>    
#include <vector>       
#include <utility>       
using namespace std;
int main() {
	vector<pair<int, char>>v;
	v.push_back({ 1,'a'});
	v.push_back({ 2,'b'});
	v.push_back({ 3,'c'});
	v.push_back({ 4,'d'});
	v.push_back({ 5,'e'});

	int pos = lower_bound(v.begin(), v.end(), pair<int, char>{ 2, 'a'}) - v.begin();//1
	cout << pos;
	pos=lower_bound(v.begin(), v.end(), pair<int,char>{ 2,'b'})-v.begin();//1
	cout << pos;
	pos = lower_bound(v.begin(), v.end(), pair<int, char>{ 2, 'k'}) - v.begin();//2
	cout << pos;
	
	return 0;
}

来解释一下如何比较的。
第一个{ 2, ‘a’},{1,‘a’}比}他小,{2,’b’}比它大,所有是1。
第二个不用说。
第三个{2,‘k’},找第一个大于等于的,{2,‘b’}小于,{3,‘c’}大于,所以是2。

所以说,如果我们对于一个pair类型要进行二分查找,让要进行比较的元素类型放在第一位,然后对val(pair类型的)的第二位最小就能够实现只对第一位进行比较。
如果pair内有字符串,且不作为比较的元素类型,字符串的话你弄个空作为第二位。

测试一下
完美!

#include <iostream>     
#include <algorithm>    
#include <vector>       
#include <utility>       
using namespace std;
int main() {
	vector<pair<int, string>>v;
	v.push_back({ 1,"12345"});
	v.push_back({ 2,"12fsd" });
	v.push_back({ 2,"sd" });
	v.push_back({ 3,"dxfghjihgfcxd" });
	v.push_back({ 4,"12bbbbajskf345" });
	v.push_back({ 5,"12dhjk345" });

	int pos = lower_bound(v.begin(), v.end(),(pair<int, string>)make_pair(2, "")) - v.begin();//1,看,精准定位
	//(pair<int, string>)make_pair(2, ""),这写法还不如直接(pair<int,string>){ele1,ele2}
	cout << pos;
	
	return 0;
}

数字转字符串,字符串转数字

后一个倒是记得,前一种好像没怎么用过

数字转字符串

头文件:<stdlib.h>
这个属于c语言的放到c++就换成头文件:<cstdlib>

函数名函数作用
itoa()将整型值转换为字符串
itoa()将长整型值转换为字符串
ultoa()将无符号长整型值转换为字符串
ecvt()将双精度浮点型值转换为字符串,转换结果中不包含十进制小数点
fcvt()以指定位数为转换精度,余同ecvt()
gcvt()将双精度浮点型值转换为字符串,转换结果中包含十进制小数点

用itoa举例一下这个函数的使用

char *itoa (int value, char *str, int base );

int value 被转换的整数,char *string 转换后储存的字符数组,是字符数组!!int radix 转换进制数,如2,8,10,16 进制等,大小应在2-36之间。

#include <iostream>     
#include <algorithm>   
#include<cstdlib>       
using namespace std;
int main() {
	int value = 1234;
	char str[5];
	cout << itoa(value, str, 10) << endl;//1234
	//注意,对于存放转换后的字符串的字符数字,大小应该比数字中字符位多一,用来存放字符串结尾的标识符'\0'
	cout << itoa(value, str, 16) << endl;//4d2
	return 0;
}

字符串转数字

c++字符串使用的,这个针对的就是字符串string了。c里面也有,把s换成a,那个针对的是字符数组(c的字符串),如果要把c的用在c++里还要把string转换成char[],等会演示一下。

#include <string>

函数函数作用
stod()字符串转double
stof()字符串转float
stoi()字符串转int
stol()字符串转long
stold()字符串转double
stoll()字符串转long long
stoul()字符串转unsigned long
stoull()字符串转unsinged long long

注意!没有unsigned double和unsigned float!!!
没有 (unsigned+浮点数) 这种类型!!!

语法规则:
stoi/l(字符串,起始位置,待转换字符串n进制(原字符串的)),将 n 进制的字符串转化为十进制

stof/d(字符串,起始位置)
总结就是可以带小数点的不存在原始字符串进制,不带的可以定义原始字符串的进制。

我们小小测试一下

#include <iostream>     
#include <algorithm>     
#include<string>
using namespace std;
int main() {
	string str = "123";
	cout << stoi(str, 0, 10) << endl;//123
	str = "123.23";
	cout << stoi(str, 0, 10) << endl;//123

	cout << stof(str, 0) << endl;//123.23
	cout << stod(str, 0) << endl;//123.23

	///str = "--123.23";
	//cout << stoi(str,0,10) << endl;直接崩掉了...c的atoi还会返回个0呢...
	
	str = "123--2";
	cout << stoi(str, 0, 10) << endl;//123
	
	str = "123--";
	cout << stoi(str, 0, 10) << endl;//123

	str = "123";
	cout << stoi(str, 0, 8) << endl; //83

	return 0;
}

再展示一下如何用c的atoi/f

#include <iostream>     
#include <algorithm>     
#include<cstdlib>
using namespace std;
int main() {
	string str = "123.12";
	cout << atoi(str.c_str()) << endl;//123
	cout << atof(str.c_str()) << endl;//123.12

    str = "--123.12";
	cout << atoi(str.c_str()) << endl;//0
	cout << atof(str.c_str()) << endl;//0

	str = "123.12--";
	cout << atoi(str.c_str()) << endl;//123
	cout << atof(str.c_str()) << endl;//123.12
	
	str = "123.--12";
	cout << atoi(str.c_str()) << endl;//123
	cout << atof(str.c_str()) << endl;//123
	return 0;
}
  • 5
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值