范围for循环
:避免在遍历STL变量时使用迭代器,for()内为单个变量:被迭代对象
,实际上迭代范围等效于,数组:[0,size()-1]
,STL类:[迭代对象.begin(), 迭代对象.end()]
string str[]={"aa","bb","cc"};
for(auto e : strs)
cout<<e<<endl;
输出容器指定位置的元素!!!
(1)vector和string:可以直接用下标[i]
直接访问指定idx的元素
。
(2)map和set:可以使用std::next(it, n)
返回it迭代器后n位元素的迭代器
。
1 STL标准库
对于STL,我们强调使用方法,并不强调原理。下面会介绍很多C++ STL库里面的模板,在编程中STL犹如神器,实用简洁好用。要熟悉vector、string、map、stack、queue等的使用。
模板template :
template <class 模板名>
template <class T>
void fn(T &a,T &b);
迭代器iterator: 类似指针,不支持<和>的比较
,支持==和!=比较
类::iterator 迭代器变量;
vector<int> v={1,2,3};
vector<int>::iterator it=v.begin();
*it; // 1
排序sort:对数组型(index)或容器型(iterator)对象排序,传入比较函数cmp(默认升序)。
sort(first,last,cmp);
bool cmp(typename a,typename b){
if(axxxb)
return True/False;
}
1.1 vector
vector为可变长数组(动态数组)
,定义的vector数组可以随时添加数值和删除元素。
用途:做为动态数组
存储不确定个数的数据。
- 头文件
#include <vector>
- 定义
//一维数组
vector<typename> name;
// typename可以是int、double、char等基本类型
vector<int> v_int(n,0);//初始长度为n,所有元素为0
vector<string> v_string(n,"");//初始长度为n,所有元素为空串
//二维数组(两个维度都变长),也可以是STL容器类型,如vector等
vector<vector<int> > name; //注意>>之间的空格
//二维数组(行数为n,列数变长),name[0]~name[n-1]是n个变长数组
vector<typename> name[n];
- 访问:下标访问v[i]、 迭代器访问*it。只有vector和string中才允许使用迭代器+整数这种写法,v.begin()+i
//1.下标访问(和数组下标一样)
vector<typename> v;
v[index];
//2.迭代器访问(和指针一样)
vector<typename>::iterator it;
*it;//*it就是v中it指向的元素(同*p)
v.begin();//返回首元素的迭代器(通俗来说就是地址)
v.end();//返回最后一个元素后一个位置的迭代器(地址)
v[i] 等价于 *(v.begin()+i)
- 常用函数:
begin():返回首元素的迭代器(通俗来说就是地址)
vector<int> v={1,2,3};
*(v.begin());
// 1
end():返回最后一个元素后一个位置
的迭代器(地址)
vector<int> v={1,2,3};
*(v.end()-1);
// 3
push_back(x):vector尾部添加一个元素x。如果需要在容器中添加构造参数的元素,可以优先考虑使用 emplace_back(x)
,可以更高效地避免复制直接将x加入容器,而push_back(x)是将x的副本插入容器。
vector<int> v={1,2,3};
v.push_back(4)
//v={1,2,3,4}
pop_back():删除尾部最后一个元素。
vector<int> v={1,2,3};
v.pop_back()
//v={1,2}
size():返回实际数据个数(unsigned类型),但用%d不会有很大问题。
vector<int> v={1,2,3};
v.sizr();
//3
clear():清除所有元素。
vector<int> v={1,2,3};
v.clear();
//v={ }
insert(iterator, x):向迭代器iterator位置插入一个元素x。
vector<int> v={1,2,3};
v.insert(v.begin(), 0);
//v={0,1,2,3}
erase():erase(it)删除迭代器it处单个元素,erase(first,last)删除迭代器[first,last)
范围内所有元素。
vector<int> v={1,2,3};
v.erase(v.begin());
//v={2,3}
vector<int> v={1,2,3};
v.erase(v.begin(),v.begin()+2);
//v={3}
vector<int> v={1,2,3};
v.erase(v.begin(),v.end());//[v.begin(),v.end())
//v={ }
1.2 string
string是一个字符串类(必须用双引号“”),和char[]型字符串类似。但操作更加简洁。
- 头文件
#include<string>
//注意#include<string>和#include<cstring>不一样
- 定义
string str;//默认初始化为空字符串
- 访问:下标访问str[i]、 迭代器访问*it。只有vector和string中才允许使用迭代器+整数这种写法,v.begin()+i
//1.下标访问
string str="yzr";
str[i];
//第i个字符
//2.迭代器访问
*(str.begin()+i);
//第i个字符
- 读取:输入hello world
①空格分隔:“hello”, “world”,cin>>遇到“空格”,“TAB”,"回车"都结束。在#include<iostream>中
string s1;
cin>>s1;
//s="hello"
②空格不分隔:“hello world”,getline(cin,s)可以接收空格,并且输出。在#include<string>中。
string s1;
getline(cin, s);
//s="hello world"
注意:getline(cin,string)和cin.getline(char*,length)不同,前者是操作string在#include<string>中没后者操作char[],在#include<iostream>中。
- 常用函数:
操作符+=:拼接字符串。同append()
string str="yzr";
str+="hello";
//str="yzrhello"
str.append("abc");
//"yzrabc"
操作符==、!=、<、<=、>、>=:字典序比较。
("aa"=="bb");
//false
操作符=:直接赋值。
string str="yzr";
str="hello"
//"hello"
length()或size():返回string的字符个数。
string str="yzr";
str.size();
// 3
str.lenth();
// 3
insert(pos,string):insert(pos,string)在pos处插入string.
string str="yzr";
str.insert(0,"hello");
//"yzrhello"
erase():删除单个元素 / 删除[first,last)区间内所有元素/删除pos开始的n个字符。
string str="yzr";
str.erase(str.begin());
//"zr"
string str="yzr";
str.erase(str.begin(),str.end());
//""
string str="yzr";
str.erase(0,2);
//"r"
claer():清空string。
string str="yzr";
str.clear();
//""
substr(pos,n):取pos开始长度为n的子串。
string str="yzr";
str.substr(0,2);
//"yz"
find(s, pos):返回从pos开始匹配到字符串s的位置,若未匹配到返回-1。
string str="hello world";
str.find("orld", 0);
//6
replace(pos,n,str):把当前字符串从索引pos开始的n个字符替换为str。
s.replace(pos,n,str);
C++ string技巧:
1 删除字符串内重复字符
string str="aadfgggh";
//去重复
sort(str.begin(),str.end());
str.erase(unique(str.begin(),str.end()),str.end());
2 删除字符串内某个指定字符
string str="aadfgggh";
str.erase(remove(str.begin(),str.end(),'a'),str.end()); //在容器中, 删除[begin,end)之间的所有值等于'a'的值.
#include<string>
string::erase(begin,end):删除[begin,end)之间的所有值
#include<algorithm>
remove(begin,end,val):
可以从它的前两个正向迭代器参数指定的序列中移除和第三个参数相等的对象。基本上每个元素都是通过用它后面的元素覆盖它来实现移除的。
返回的迭代器指向通过这个操作得到的新序列的尾部,所以可以用它作为被删除序列的开始迭代器来调用 samples 的成员函数 erase()。
1.3 map
map翻译为映射,也是常用的STL容器。 用途:
①将任何基本类型(包括STL容器)映射到任何基本类型(包括STL容器)
,如需要建立字符(或字符串)与整数之间映射的题目,也就可以建立string型到int型的映射,使用map 可以减少代码量。
②判断给定的一些数字在某个文件中是否出现过。
判断大整数或者其他类型数据是否存在/出现过的题目,可以把map当<string,bool>数组用。字符串和字符串的映射也有可能会遇到。按照正常的思路,可以开一个 bool型 hashTable[max size],通过判断 hashTable[x]为 true 还是 false来确定x是否在文件中出现。但是这会碰到一个问题,如果这些数字很大(例如有几千位),那么这个数组就会开不了。而这时 map 就可以派上用场,因为可以把这些数字当成一些字符串,然后建立string至int的映射(或是直接建立int至int的映射)。
延伸:
map的键和值是唯一的,而如果一个键需要对应多个值,就只能用multimap。
- 头文件
#include<map>
- 定义
map<typename1,typename2> mp;//存储多个key-value对,key是唯一的不重复
//eg. map<string,int> m={{"a",1},{"b",2}};
- 访问:下标访问value=mp[key] / 迭代器iterator访问(
map内部使用红黑树实现,会自动按key进行升序排序
)
//下标访问mp[key]
map<string,int> m={{"a",1},{"b",2}};
mp["a"];
// 1
//迭代器访问
map<typename1,typename2>::iterator it;
for(it=mp.begin();it!=mp.end();it++){
it->first;//访问key
it->second;//访问value
}
//迭代器访问
- 常用函数:
find(key):在map中 判断一个key是否存在,可以使用find或count。find找到返回第一个key元素迭代器,没找到返回迭代器end(),count求key的个数。
map.find(key) != map.end();
map.count(key) != 0;
erase(key/it):删除键为key的键值对。/删除迭代器it处的键值对。/删除迭代器区间内的键值对[first,last)
map.erase(key);
map.erase(it);
map.erase(first,last);
size():map内返回键值对个数。
map.size()
clear():清空map。
map.clear();
map统计string出现的次数:(hash) :map<string,int>
int n;
string str;
cin >> n;
map<string, int> mp;
for (int i = 0; i < n; i++) {
cin >> str;
if (mp.find(str) == mp.end())//str没有在mp中出现过,就置1
mp[str] = 1;
else//mp中找到str,就++
mp[str]++;
}
unordered_map
无序map!!!,当你使用map,但map的自动排序会破坏你的构造顺序时
,可以使用unordered_map,使用方法和map一样!
1.4 pair
存储两个不同类型的元素,可以看作单个键值对(做为map的元素)
。
//头文件
#include<utility>
//定义
pair<typename1,typename2> p;
//初始化
pair <string,int> p("a",1)
p = make_pair("a",1);
//访问
p.first;//获取key
p.second;//获取value
//做为元素 插入map
mp.insert(make_pair("a",1));
1.5 stack
先进后出,用途:stack用来模拟实现一些递归
,防止程序对栈内存的限制而导致程序运行出错。
- 定义
#include<stack>
- 定义
stack<typename> st;
- 访问:先进后出.
top():只能用top()访问栈顶元素。(访问)
push():将元素压入栈顶。(插入)
pop():将栈顶元素弹出。(删除)
stack<int> st;
st.push(1);st.push(2);st.push(1);
//{1,2,3}
st.pop();
//{1,2}
st.top();
//2
- 常用函数
//empty():检测栈是否为空。
st.empty();
//size():返回栈元素个数。
st.size()
1.6 queue
先进先出,用途:
①广度优先搜索
时,用queue实现队列。
②使用front()和pop()函数前,必须用empty()判断队列是否为空
,否则可能因为队空而出现错误。
延伸
:queue相似的STL容器:双端队列(deque)、优先队列(priority_queue),前者是首尾皆可插入和删除的队列,后者是使用堆实现的默认将当前队列最大元素置于队首的容器。
- 头文件
#include<queue>
- 定义
queue<typename> q;
- 访问:
先进先出。
push()在队尾添加元素(插入)
pop()取出队首元素(删除)
front()访问队首元素。(访问)
back()访问队尾元素。(访问)
queue<int> q;
q.push(1);q.push(2);q.push(3);
//{1,2,3}
q.pop();
//{2,3}
q.front();
//1
q.back();
//2
- 常用函数
//empty():检测队列是否为空。
q.empty();
//size():返回队列元素个数。
q.size()
1.7 set
用途:自动去重
、按值大小自动排序
的vector。
- 头文件
#include<set>
- 定义
set<int> s={1,2,3,4,5,6};
- 访问:范围for,
除了vector和string,其他STL容器不支持it++
for(auto it:s) cout<<it<<' ';
需要控制结尾的空格" “和换行”\n"时,需要使用迭代器(注意在开始控制输出,第一个元素前不输出
)
for(set<int>::iterator it=s.begin();it!=s.end();it++){
if(it!=s.begin()) cout<<" ";
cout<<*it;
}
- 常用函数:
//插入
s.insert(item);//如果item已经存在,则不再重复插入
//统计元素个数
s.count();
//统计set内元素个数
s.size();
//返回指定元素的迭代器,找不到则返回s.end()
s.find(item);
//set的 开始位置 和 末尾后一个位置 迭代器
s.begin();
s.end();
2 algorithm算法
//头文件
#include<algorithm>
①max(x,y)
和min(x,y)
:返回x,y中的最大/最小值
②abs(x)
:返回x的绝对值,x必须为int型。
若需要浮点型,用#include<cmath>
下的fbas()
③swap(x,y)
:交互x,y的值。
④reverse(it1,it2)
:将容器在迭代器/数字在指针[it1,it2)之间的元素反转顺序。
容器在迭代器:[begin(),begin()+k)
数字在指针:[a,a+k)
⑤fill(首元素地址,尾元素地址,num)
:将数组指针[首元素地址,尾元素地址]之间的元素填充为num。
⑥sort(首元素地址,尾元素地址,cmp函数)
:将数组指针[首元素地址,尾元素地址]之间的元素排序,cmp不是必须的,默认升序。sort具体使用方法见保研机试——1基础算法。
⑦lower_bound(first,last,val)
和upper bound(first,last,val)
:求有序容器[first,last)范围中第一个大于等于 / 大于val的元素位置,容器返回迭代器,数组返回指针。