第二周学习笔记
STL
泛类型
template <typename T>
T => 泛类型
sample:
T max(T x,T y)
————————————————————————————————————————————————
课堂样例
#include <bits/stdc++.h>
using namespace std;
template <typename T>
T my_max(T x,T y){
return x > y ?x :y;
}
struct point{
int x,y;
point(int a,int b):x(a),y(b){} ## 构造函数(个人理解为定义一种输入point的格式,待之后继续学习)
bool operator > (point j){ ## 重载(>),因为max函数里的(?:)是根据true和false来判断的,所以重新定义的(>)要用bool函数(内联写法)
if(x > j.x) return true;
if(x == j.x){
if(y > j.y)
return true;
}
return false;
}
bool operator < (point j); ## 重载(>)(外联写法)先声明
};
bool point::operator < (point j){ ## 重载(>)(外联写法)主体代码
if(x < j.x) return true;
if(x == j.x){
if(y < j.y)
return true;
}
return false;
}
ostream& operator << (ostream& out,point p){ ## 重载(<<)
out << p.x << "," << p.y << endl;
return out;
}
istream& operator >> (istream& in,point& p){ ## 重载(>>)
in >> p.x >> p.y;
return in;
}
int main()
{
cout << my_max(12,3) << endl;
cout << my_max(12.33,3.21) << endl;
cout << my_max('A','Z') << endl;
cout << my_max("abc","abz") << endl;
point p1(3,1),p2(3,4);
cin >> p1 >> p2;
cout << my_max(p1,p2) << endl;
return 0;
}
vector 动态数组
头文件
#include <vector>
定义vector数组
## 定义int型数组
vector <int> a; ## a为空
vector <int> b(a); ## 用a定义b
vector <int> a(100) ; ## a有100个为0的元素
vector <int> a(100, 6); ## a有100个为6的元素
## 定义string型数组
vector <string> a(10," null"); ## 10个值为null的元素
vector <string> vec(10,"hello"); ## 10个值为hello的元素
vector <string> b(a. begin(), a. end()); ## b是a的复制
## 定义结构型数组
struct point { int x, y; };
vector <point> a;
## 定义二维数组
vector <point> a[maxn]; ## 行静态,列动态
访问方式
## 下标访问
cout << a[i] << endl;
## 指针访问,用于以下情况
cout << *a.begin() << endl;
cout << *a.end() << endl;
## 迭代器访问
vector<int>::iterator it;
for(it=vec.begin();it!=vec.end();it++)
cout<<*it<<endl;
## 指针移动访问(最好别用,优先用迭代器,这个方法不知道合不合法,其实和下标访问区别不大)
vector<int> *p = &a;
for(int i = 0;i < a.size();i++)
cout << (*p)[i] << endl;
常规操作(一些自带的函数)
基础函数
a.push_back(); ## 尾部添加元素
a.size(); ## 返回元素个数大小
a.empty(); ## 返回是否为空,bool值
a.resize(); ## 改变数组大小,变小删除后面元素,变大自动补0
a.clear(); ## 清空
————————————————————————————————————————————————————————————————————————————————————————————————————
插入与删除
a.insert(a.begin() + i,k); ## 在下标为i的元素前插入k
a.insert(a.end(),k,m); ## 尾部插入k个m
a.pop_back(); ## 删除尾部元素
a.erase(a.begin() + i); ## 删除下标为i的元素(下标为2)
a.erase(a.begin() + i,a.begin() + j); ## 删除区间[i,j - 1]内的元素
————————————————————————————————————————————————————————————————————————————————————————————————————
翻转与排序
reverse(a.begin() + i,a.begin() + j); ## 翻转区间[i,j - 1]内的元素
sort(a.begin() + i,a.begin() + j); ## 对区间[i,j - 1]内的元素排序
可直接用的比较函数
less(降序)
greater(升序)
————————————————————————————————————————————————————————————————————————————————————————————————————
寻找
vector<int>::iterator result = find(L.begin( ) + i,L.begin( ) + j,m); ## 在区间[i,j - 1]内寻找m
cout << result - a.begin() << endl; ## 输出m值的下标位置
String 字符串类型
头文件
#include <string>
定义String
string str ## 生成空串
string s = "12346789"; ## 直接赋值定义
string s(a) ## 以a定义s
string s(a,strbegin) ## 以a从strbegin到结尾的串定义s,且strbegin只能为下标,不能为地址
string s(a,strbegin,strlen) ## 以a从strbegin开始长度为strlen的串定义s,且strbegin只能为下标,不能为地址
string s(n,c) ## 生成num个c字符的字符串
访问方式
## 下标访问
cout << s[i] << endl;
## 指针访问,用于以下情况
cout << *s.begin() << endl;
cout << *s.end() << endl;
## 迭代器访问
string::iterator it;
for(it = s.begin();it != s.end();it++)
cout << *it << endl;
常规操作(一些自带的函数)
运算符的直接使用
+: 尾部添加(字符字符串皆可)
>: 直接比较大小(按字典循序,只能和字符串做比较)
<: 同上
>=: 同上
<=: 同上
————————————————————————————————————————————————————————————————————————————————————————————————————
基础函数
s.append(a); ## 将a添加到s尾部,a只能为字符串
s.length(); ## 返回s的长度(及大小)
s.size(); ## 返回s的大小(及长度)
s.empty(); ## 返回s是否为空,返回值为bool类型
————————————————————————————————————————————————————————————————————————————————————————————————————
插入、删除、替换
s.insert(s.begin() + i,a); ## 在下标为i的元素前添加a,a只能为字符
s.erase(s.begin() + i); ## 删除下标为i的元素
s.erase(s.begin() + i,s.begin() + j); ## 删除区间[i,j - 1]内的元素
s.replace(strbegin,strlen,a); ## 从下标strbegin开始长度为strlen的字符替换为a,a只能为字符串
————————————————————————————————————————————————————————————————————————————————————————————————————
翻转与排序
reverse(s.begin() + i,s.begin() + j); ## 翻转区间[i,j - 1]内的元素
sort(s.begin() + i,s.begin() + j); ## 对区间[i,j - 1]内的元素排序
可直接用的比较函数
less(降序)
greater(升序)
————————————————————————————————————————————————————————————————————————————————————————————————————
寻找与比较
s.find(a) ## 在s中寻找a(a可以为字符串也可以为字符),如果找到返回找到的第一个下标的值,不能则返回2^32 - 1
s.rfind(a) ## 在s的末尾开始找a,其他同上
s.find(a,strindex) ## 在s下标为strindex的地方开始找a
s.compare(a) ## 将s与a比较(字典循序),a只能为字符串,大于返回1,等于返回0,小于返回-1
stack 栈(先进后出)
头文件
#include <stack>
定义stack
stack <int> a;
访问方式
对于stack而言,中间的元素是无法访问的,只能通过a.top()访问栈顶元素
a.top();
常规操作(一些自带的函数)
基础函数
a.push(x); # 将x放到栈顶
a.pop(); # 删除栈顶元素
a.size(); # 返回栈内元素个数
a.empty(); # 返回栈是否为空
由于栈的使用对空间占用较大(深度大了不能及时释放),所以一下两点可以解决
1.在程序中调用大系统的栈,依赖于系统和编译器(不推荐)
2.手工写栈(没试过,但感觉数组可以实现,自己封装一下应该就ok)
queue 队列(先进先出,公平原则)
头文件
#include <queue>
定义queue
queue <int> a;
访问方式
同理,对于queue而言,中间的元素是无法访问的,只能通过a.front()访问栈顶元素
a.front();
常规操作(一些自带的函数)
基础函数
a.push(x); # 将x放到队尾
a.pop(); # 删除队首元素
a.back(); # 返回队尾元素
a.size(); # 返回队列内元素个数
a.empty(); # 返回队列是否为空
关于优先队列内容与队列高度相似(除了访问队首要使用 a.top(),其他并无不同),关键在于如何定义排序规则,在下面的Struct 结构体与stl的结合中有相关赘述(只是板块分的不太好),此处就不再说了。
set 集合(去重性,有自动排序的功能)
头文件
#include <set>
定义set
set <int> a;
访问方式
迭代器访问
set <int> :: iterator it;
for(it = a.begin();it != a.end();it++){
cout << *it << endl;
}
常规操作(一些自带的函数)
基础函数
a.insert(x); # 插入x元素
a.erase(a.begin()); # 注意此处函数内要填迭代器,a.begin()实质上貌似就是迭代器
a.clear(); # 清空
a.empty();
a.size();
a.find(k); # 返回一个迭代器,指向键值k
a.lower_bound(k); # 返回一个迭代器,指向键值不小于k的第一个元素
a.upper_bound(k); # 返回一个迭代器,指向键值大于k的第一个元素
multiset为能容纳相同元素的集合,因不了解(用的少了当然不知道),暂不做记录
set中还有极为好用的求并集,交集,差集等操作,自己用的不熟,此处贴大佬blog
https://blog.csdn.net/zangker/article/details/22984803
map 映射(映射,去重性,有自动排序的功能)
头文件
#include <map>
定义map
map <int,int> a;
赋值
a[x]=c;
访问方式
没研究出来,打扰了
}
常规操作(一些自带的函数)
基础函数
a.insert(x); # 存在这个东西,但写起来贼麻烦,直接用赋值即可
a.erase(a.begin()); # 注意此处函数内要填迭代器,a.begin()实质上貌似就是迭代器
a.clear(); # 清空
a.empty();
a.size();
a.find(k); # 返回一个迭代器,指向键值k 找不到就map::end()
multimap存在,但不知道怎么用,暂不整理
一些写题中学到的东西
Struct 结构体与stl的结合(排序)
vector
自己写cmp函数
bool GreaterSort (Node a,Node b) { return (a.x>b.x); }
bool LessSort (Node a,Node b) { return (a.x<b.x); }
sort(a.begin(),a.end(),GreaterSort);
————————————————————————————————————————————————————————————————————————————————————————————————————
重载<运算符
struct node ## 内联写法
{
int x, y;
bool operator < (const node & o) const
{
return x<o.x;
}
};
priority_queue
调用默认排序函数
priority_queue<int,vector<int>,greater<int> >q1;
priority_queue<int,vector<int>,less<int> >q1;
————————————————————————————————————————————————————————————————————————————————————————————————————
自定义比较函数
struct cmp1{ 重载()运算符
bool operator()(node a,node b)
{
if(a.x == b.x) return a.y > b.y;
return a.x > a.x;
}
};
priority_queue<node,vector<node>,cmp1> q;
————————————————————————————————————————————————————————————————————————————————————————————————————
重载<运算符
struct node{
int x,y;
friend bool operator<(node a,node b) ## 必须为友元关系
{
return a.x>b.x;
}
};
priority_queue <node> q;
set
调用默认排序函数
set<int,greater<int> > Set;
set<int,less<int> > Set;
————————————————————————————————————————————————————————————————————————————————————————————————————
自定义比较函数
struct intcmp {
bool operator() (const int& a, const int& b) const{
return a > b;
}
};
struct strcmp
{
bool operator() (const string& a, const string& b) const {
return a < b;
}
};
set<int, intcmp> Set
————————————————————————————————————————————————————————————————————————————————————————————————————
重载<运算符
struct node
{
int x,y;
bool operator < (const node &s) const {
if(x!=s.x) return y < s.y;
else return x<s.x;
}
};
set <node> Set;
map
map也有自动排序的特性,但现目前来讲没有特别用到的地方,暂不整理
总结:
由于vector支持sort排序,大多是时候使用sort的自定义排序会较为便捷。
而set和priority_queue无法使用sort排序,个人角度而言,重载<运算符可能使用起来较为方便。
一些备注:
1.如不使用写比较函数的方式,重载的都是 < 运算符。
2.而使用比较函数的话,除了vector,都得重载 () 并将比较函数放入定义中。
3.比较值得注意的是,priority_queue的 < 运算符重载必须得写成友元。
pair
个人是当做封装好的二元素结构体或者二元素的映射用的,当和stl里的容器结合时,用自定义的结构体往往要自己写重载,比较麻烦,而pair就能比较好的解决这个问题。
网上偷的图,一些基础用法就在里面了
1.创建
pair <string,string> a; # 普通类型的创建
pair <string, vector<int> >; # 其和容器也是能互相套用的
pair <int,int> a = make_pair(a,b); # 自行尝试的创建方式,也是可以的
2.访问
访问方式与结构体类似,此处不再赘述
## count函数
**algorithm**头文件定义了一个**count**的函数,其功能类似于find。这个函数使用一对迭代器和一个值做参数,返回这个值出现次数的统计结果。
**count**
```cpp
vector中的用法
vector <int> a;
count(a.begin(),a.end(),x);
map中的用法
map <int,int> a;
a.count(x);
set中的用法 # 两种皆可
set <int> a;
count(a.begin(),a.end(),x);
a.count(x);
list中的用法
list <int> a;
count(a.begin(),a.end(),x);
以上容器是做过实验的,queue确认两种写法都不行,而还有一些容器例如multimap和multiset应该与上述一致,因时间原因以及使用次数原因,暂不做整理,以后用到继续补充
count_if
bool greater10(int x){
return x >10;
}
vectoe中的用法
vector <int> a;
vector <int> :: size_type sum = count_if(a.begin(),a.end(),greater10);
set中的用法
set <int> a;
set <int> :: size_type sum = count_if(a.begin(),a.end(),greater10);
list中的用法
list <int> a;
list <int> :: size_type sum = count_if(a.begin(),a.end(),greater10);
count_if貌似在map中无法使用?我并没有找到合适的写法,且实用性不高,所以不做深究
总结:
实际上,count的计数功能在日常使用中用的还是比较少的,他目前对于我而言用的最多的地方是判断这个元素在这个容器中是否存在,对于set和map两个容器,因为他们的去重性,count返回的结果只有可能是0或1,起到了一个不错的判断作用,而判断他们的值是否为0来确认这个值是否出现过(map中),因为0这个值得特殊性往往会导致一些细节问题,而count能比较好的避免这个问题。
重点:它写起来比find函数简单,find函数还要写一个 !=a.end() ,懒人感觉写起来有点麻烦(狗头)
find函数
在string中
string中find()返回值是字母在母串中的位置(下标记录),如果没有找到,那么会返回一个特别的标记npos
s.find(x) == string::npos; 没找到
s.find(x) != string::npos; 找到了
string::npos 也可以写为s.npos
也可以自己定义找第一次出现或者最后一次出现的位置
s.find_first_of(x);
s.find_last_of(x);
亦可以定义找某一特定位置后的第一次出现位置
s.find(x,index) x为要找的东西,index为下标
还有反向查找
s.rfind(x)
还有最后一个网上找到的,估计不常用
查找所有子串在母串中出现的位置
//查找s 中flag 出现的所有位置。
flag="a";
position=0;
int i=1;
while((position=s.find(flag,position))!=string::npos)
{
cout<<"position "<<i<<" : "<<position<<endl;
position++;
i++;
}
来自 https://www.cnblogs.com/wkfvawl/p/9429128.html
在stl中
find的用法与count比较一致(**a.find()**此处不再赘述)
find(a.begin(),a.end(),x);
前两个参数一般为头和尾,x为要查找位置的元素
如果找到则返回迭代器,没找到则返回end()
find(a.begin(),a.end(),x) != a.end();
要注意的是,因为一部分容器并非顺序容器,所以不能单纯的当做指针来进行加减法
sort函数
c++内置的优化版快排,简单实用
一般的结构体的sort排序中,以下两种方式
自己重载()运算符,
struct comp{
bool operator()(const int &a1, const int &a2){
return (a1%10)<(a2%10);
}
};
sort(a,a+x,comp());
————————————————————————————————————————————————————————————————————————————————————————————————————
直接写一个cmp函数
bool comp(const int &odd1,const int &odd2)
{
return odd1>odd2;
}
sort(a,a+x,comp);
next_permutation() 排列组合函数
要点
1.初始序列必须为字典循序最小的排列,可用sort先行排序
2.每次排序会改变原序列内的值的位置
定义
bool next_permutation(BidirectionalIterator firest,BidirectionalIterator last);
bool next_permutation(BidirectionalIterator firest,BidirectionalIterator last,Comparecomp);
对于这个函数的比较函数没啥了解,且因时间有限,暂不做整理,以后有机会补上
最后贴上大佬blog地址 https://blog.csdn.net/qq_40486952/article/details/83280212
STL中的迭代器
迭代器实质上是一个指针,但并非所有迭代器都可以进行加减法
在vector,string,deque中是有加减法的
而在map,set,multimap,multiset,list中是不能进行加减法的
但自加自减貌似都是可行的
关于自加自减这一块的引用和临时对象这一块没啥了解,暂不深究
貌似引用容易带来风险,蒟蒻对这一块真的不懂
个人建议自加自减写成it++和it–的写法
以上摘自 https://blog.csdn.net/lym940928/article/details/88905175?utm_source=app
逆波兰表达式(后缀表达式)
关于逆波兰表达式,在做带括号的算式题的时候了解并学习的。
刚开始理解为一个转换过程,能将中缀表达式的计算优先级进行从左往右的重新排列,并通过特定的方法计算。
在进行实践之后,个人感觉就是把中缀表达式的计算过程拆分为重定优先级和计算的过程。
不知道怎么来的,但姑且算获得了一个解决算式题型的思路。(后重写了中缀表达式直接计算的代码)
由于大佬总结的已经很好,直接贴blog地址。
中缀转后缀 https://blog.csdn.net/coder_dacyuan/article/details/79941743
后缀的计算 https://blog.csdn.net/sjhdxpz/article/details/81210448
结构体中的自定义函数
暂时开一块,是接下来要了解的方向。