PAT一些 C++实用的方法
1、C++方法
1、accumulate
#135分发糖果
int candy(vector<int>& ratings) {
int size=ratings.size();
vector<int> res(size,1);
for(int i=1;i<size;i++){
if(ratings[i]>ratings[i-1]){
res[i]=res[i-1]+1;
}
}
for(int i=size-1;i>0;i--){
if(ratings[i-1]>ratings[i]){
res[i-1]=max(res[i-1],res[i]+1);
}
}
return accumulate(res.begin(),res.end(),0); //0表示从0开始累加 可以自定义
}
accumulate函数将它的一个内部变量设置为指定的初始值,然后在此初值上累加输入范围内所有元素的值。accumulate算法返回累加的结果,其返回类型就是其第三个实参的类型。
可以使用accumulate把string型的vector容器中的元素连接起来:
string sum = accumulate(v.begin() , v.end() , string(" "));
这个函数调用的效果是:从空字符串开始,把vec里的每个元素连接成一个字符串。
2、String 和char数组转换 strcpy() c_str()
string str1 = "ABCDEFG";
char a[20];
strcpy(a,str1.c_str());//用到 c_str()函数
string str2(a); //char转为string
也可以直接
string str3=a; //char能直接转为string
2、sscanf() sprintf()
sscanf() sprintf() 搭配起来用可以判断字符串是否为数值,详情见PAT 1108
char a[50], b[50];
double temp = 0.0;
scanf("%s", a);
sscanf(a, "%lf", &temp);
sprintf(b, "%.2f",temp);
int flag = 0;
for(int j = 0; j < strlen(a); j++)
if(a[j] != b[j]) flag = 1;
可以直接判断该字符串是否为数值!!
#include<cstdio>
#include<string>
#include<cstring>
#include<iostream>
using namespace std;
int main(){
string x="-123.232";
double temp;
//printf("%s",x.c_str()); //c_str 一般用于输出的时候 像这样,其他赋值给char好像不对
//sprintf(x.c_str(),"%lf",temp); //这样写就报错
char a[50];
strcpy(a,".789a-1.10"); // include cstring ,赋值给char数组
sscanf(a,"%lf",&temp);
cout<<temp<<endl; //0.789
strcpy(a,"aai.789a-1.10"); // include cstring ,赋值给char数组
sscanf(a,"%lf",&temp);
cout<<temp<<endl; //2.07468e-317
strcpy(a,"-1.10"); // include cstring ,赋值给char数组
sscanf(a,"%lf",&temp);
cout<<temp<<endl; //-1.1
strcpy(a,".10"); // include cstring ,赋值给char数组
sscanf(a,"%lf",&temp);
cout<<temp<<endl; //0.1
strcpy(a,".1.2"); // include cstring ,赋值给char数组
sscanf(a,"%lf",&temp);
cout<<temp<<endl; //0.1
char b[50];
double c=11.456123123;
sprintf(b,"%.2f",c);
printf("%s\n",b); //11.46
c=12;
sprintf(b,"%.2f",c);
printf("%s\n",b); //12.00
getchar();
return 0;
}
3、atoi() ,atof() ,stoi()函数,to_string()
和上面的sscanf()有点类似 ,一个是char数组转化为int一个为float
①atoi()的参数是 const char* ,因此对于一个字符串str我们必须调用 c_str()的方法把这个string转换成 const char类型的,而stoi()的参数是const string,不需要转化为 const char*;
char a[10];
strcpy(a,"123");
int b=atoi(a);
string c="123";
int d=stoi(c);
string z=to_string(d);//把任何类型的数字转化为string
4、lower_bound() upper_bound()
lower_bound 返回第一个大于等于 value的位置 减去 begin()就是下标
upper_bound 返回第一个大于value的位置
值得思考的是 如果要找一个数是否存在则 left<= right ,这样相等的时候return ture ,最外面return false;
class Solution { //本题为leetcode 34 在排序数组中查找元素的第一个和最后一个位置
public:
int lowbound(vector<int> &nums,int target){
int left=0,right=nums.size(); //这里最好是right=nums.size(),因为找不到的话就返回数组长度 可以来后续判定
while(left<right){ //这里是<
int mid=left+(right-left)/2;
if(target<=nums[mid]){ //这里是 <=
right=mid;
}else{
left=mid+1;
}
}
return left; //这里return left 和right都可以
}
int upperbound(vector<int>&nums,int target ){
int left=0,right=nums.size(); //这里最好是right=nums.size(),因为找不到的话就返回数组长度 可以来后续判定
while(left<right){ //这里是 <
int mid=left+(right-left)/2;
if(target<nums[mid]){ //这里是 <!!!!!!!!!!!
right=mid;
}else{
left=mid+1;
}
}
return left; //这里return left 和right都可以
}
vector<int> searchRange(vector<int>& nums, int target) {
// int left=lowbound(nums,target);
// int right=upperbound(nums, target);
// if(nums.size()==0||left==nums.size()||nums[left]!=target) return vector<int>{-1,-1};
// else{
// return vector<int>{left,right-1};
// }
int left=lower_bound(nums.begin(), nums.end(), target)-nums.begin(); //这里得出的就是数组下标
int right=upper_bound(nums.begin(), nums.end(), target)-nums.begin()-1; //这里-1是题目需要,得出的也是数组下标
if(nums.size()==0||left==nums.size()||nums[left]!=target) return vector<int>{-1,-1};
else return vector<int>{left,right};
}
};
5、isalnum() 判断是否为字母数字
int main()
{
int var1 = 'd';
int var2 = '2';
int var3 = '\t';
int var4 = ' ';
if( isalnum(var1) ) //如果 var1 是一个数字或一个字母,则该函数返回非零值,否则返回 0。
{
printf("var1 = |%c| 是字母数字\n", var1 );
}
else
{
printf("var1 = |%c| 不是字母数字\n", var1 );
}
6、 isdigit() 判断是否为数字
#include <stdio.h>
#include <ctype.h>
int main()
{
int var1 = 'h';
int var2 = '2';
//如果 var1 是一个数字,则该函数返回非零值,否则返回 0。
if( isdigit(var1) )
{
printf("var1 = |%c| 是一个数字\n", var1 );
}
else
{
printf("var1 = |%c| 不是一个数字\n", var1 );
}
if( isdigit(var2) )
{
printf("var2 = |%c| 是一个数字\n", var2 );
}
else
{
printf("var2 = |%c| 不是一个数字\n", var2 );
}
return(0);
}
var1 = |h| 不是一个数字
var2 = |2| 是一个数字
7、isalpha()判断是否为字母
#include <stdio.h>
#include <ctype.h>
int main()
{
int var1 = 'd';
int var2 = '2';
int var3 = '\t';
int var4 = ' ';
if( isalpha(var1) )
{
printf("var1 = |%c| 是一个字母\n", var1 );
}
else
{
printf("var1 = |%c| 不是一个字母\n", var1 );
}
var1 = |d| 是一个字母
var2 = |2| 不是一个字母
var3 = | | 不是一个字母
var4 = | | 不是一个字母
8、tolower()函数与toupper()函数 大小写字符转换
这两个函数的声明在头文件中
但是经过测试,如果不包含头文件仅仅有也是可以的
//定义两个字符串
string a;
string b;
//用string库,调用getline, 直接读入一整行
getline(cin,a);
getline(cin,b);
//转换大小写,可以都转换为大写,或者小写
for (int i=;i<a.length();++i){
a[i]=tolower(a[i]);
}
for (int i=;i<b.length();++i){
b[i]=tolower(b[i]);
}
9、transform()函数转化字符串大小写
#include <iostream>
#include <string>
#include <cctype>
#include <algorithm>
using namespace std;
int main()
{
string s = "Hello World";
cout << s << endl;
transform(s.begin(),s.end(),s.begin(),::toupper);//::toupper使用定义在全局空间里的
cout << s << endl;
transform(s.begin(),s.end(),s.begin(),::tolower);
cout << s << endl;
return 0;
}
2、Vector
int size=ratings.size();
vector<int> res(size,1); //创建size大小,值全为1的vecotr
vector<vector<int>> arr(m, vector<int>(n, -1)); //m*n列的数组 全为-1
return vector<int>{l + 1, r + 1}; 可以直接这样return 一个vecotr
2.1、用insert合并两个vector
vector<int>nums3;
nums3.insert(nums3.end(),nums1.begin(),nums1.end());
nums3.insert(nums3.end(), nums2.begin(),nums2.end());
2.2、emplace_back()与push_back()
该函数是 C++ 11 新增加的,其功能和 push_back() 相同,都是在 vector 容器的尾部添加一个元素。
emplace_back() 成员函数的用法也很简单,这里直接举个例子:
#include <iostream>
#include <vector>
using namespace std;
int main()
{
vector<int> values{};
values.emplace_back(1);
values.emplace_back(2);
for (int i = 0; i < values.size(); i++) {
cout << values[i] << " ";
}
return 0;
}
emplace_back() 和 push_back() 的区别,就在于底层实现的机制不同。push_back() 向容器尾部添加元素时,首先会创建这个元素,然后再将这个元素拷贝或者移动到容器中(如果是拷贝的话,事后会自行销毁先前创建的这个元素);而 emplace_back() 在实现时,则是直接在容器尾部创建这个元素,省去了拷贝或移动元素的过程
3、Sort
sort(intervals.begin(),intervals.end(),[](auto & a,auto &b){
return a[1]<b[1];
}); 这比使用cmp的方法快了10倍速
3.1 如何给unordered_map 按照value进行排序
- 需要重写sort中的排序
- unordered_map不能直接排序,需要使用pair存在vector中
#include<iostream>
#include<map>
#include<vector>
#include<unordered_map>
#include<algorithm>
using namespace std;
bool comp( const pair<int,int>& a, const pair<int,int> &b){
return a.second > b.second;
}
int main (){
unordered_map <int,int> ump;
vector<int> R ={1,2,3,2,2,4,4,1};
for(auto x:R){
ump[x]++;
}
vector<pair<int,int> > b;
for(auto x:ump){
b.push_back(x);
}
sort(b.begin(),b.end(),comp);
for(auto x:b)
cout<< x.first<<" " <<x.second <<" "<<endl;
}
4、pair的使用
4.1 基本使用 和 pair的比较
pair<T1, T2> p1; //创建一个空的pair对象(使用默认构造),它的两个元素分别是T1和T2类型,采用值初始化。
pair<T1, T2> p1(v1, v2); //创建一个pair对象,它的两个元素分别是T1和T2类型,其中first成员初始化为v1,second成员初始化为v2。
make_pair(v1, v2); // 以v1和v2的值创建一个新的pair对象,其元素类型分别是v1和v2的类型。
p1 < p2; // 两个pair对象间的小于运算,其定义遵循字典次序:如 p1.first < p2.first 或者 !(p2.first < p1.first) && (p1.second < p2.second) 则返回true。
p1 == p2; // 如果两个对象的first和second依次相等,则这两个对象相等;该运算使用元素的==操作符。
p1.first; // 返回对象p1中名为first的公有数据成员
p1.second; // 返回对象p1中名为second的公有数据成员
4.2 进阶使用 搭配容器
//使用容器来存储pair
i=1 ,j=2;
stack<pair<int, int>> island;
island.push({i, j}); //可以直接这样存取
//读取的方式
auto [r, c] = island.top(); //此时r=1,c=2;
5、String
5.1、 erase删除最后一个字符
res.erase(res.end()-1);
5.2、创建string的特殊方法,搭配vector使用
string str("123456");
cout<<str<<endl;
str=string(4,'0'); 这里必须是 char类型
cout<<str<<endl;
string sb=string(4,'-');
cout<<sb<<endl;
vector<string> board(4,string(4,'1'));
for(int i=0;i<4;i++){
cout<<board[i]<<endl;
}
123456
0000
----
1111
1111
1111
1111
6、struct
6.1 初始化struct
在建立结构体**数组**时,如果只写了带参数的构造函数将会出现数组无法初始化的错误!!!各位同学要牢记呀!
下面是一个比较安全的带构造的结构体示例
struct node{
int data;
string str;
char x;
//注意构造函数最后这里没有分号哦!
node() :x(), str(), data(){} //无参数的构造函数数组初始化时调用
node(int a, string b, char c) :data(a), str(b), x(c){}//有参构造
}N[10];
7、set
7.1、重载<实现结构体排序
struct node{
int value,cnt;
node(int a,int b):value(a),cnt(b){}
node(){}
bool operator < (const node &a) const{
if(cnt!=a.cnt) return cnt>a.cnt;
else return value<a.value;
}
};
8、multiset
支持重复元素的set
9、priority_queue
- top 访问队头元素
- empty 队列是否为空
- size 返回队列内元素个数
- push 插入元素到队尾 (并排序)
- emplace 原地构造一个元素并插入队列
- pop 弹出队头元素
- swap 交换内容
9.1、基本数据类型使用
//升序队列,小顶堆 greater就是>大于符号,实现递减关系,得到最后一个是最小的,所以就是最小堆
priority_queue <int,vector<int>,greater<int> > q;
//降序队列,大顶堆 less就是<小于符号,实现递增关系,得到最后一个是最大的,所以就是大顶堆
priority_queue <int,vector<int>,less<int> >q;
//greater和less是std实现的两个仿函数(就是使一个类的使用看上去像一个函数。其实现就是类中实现一个operator(),这个类就有了类似函数的行为,就是一个仿函数类了)
例子
#include<iostream>
#include <queue>
using namespace std;
int main()
{
//对于基础类型 默认是大顶堆
priority_queue<int> a;
//等同于 priority_queue<int, vector<int>, less<int> > a;
// 这里一定要有空格,不然成了右移运算符↓↓
priority_queue<int, vector<int>, greater<int> > c; //这样就是小顶堆
priority_queue<string> b;
for (int i = 0; i < 5; i++)
{
a.push(i);
c.push(i);
}
while (!a.empty()) //输出4 3 2 1 0
{
cout << a.top() << ' ';
a.pop();
}
cout << endl;
while (!c.empty()) //输出0 1 2 3 4
{
cout << c.top() << ' ';
c.pop();
}
cout << endl;
b.push("abc");
b.push("abcd");
b.push("cbd");
while (!b.empty()) //输出cbd abcd abc
{
cout << b.top() << ' ';
b.pop();
}
cout << endl;
return 0;
}
9.2、用pair做优先队列元素的例子
pair的比较,先比较第一个元素,第一个相等比较第二个。
#include <iostream>
#include <queue>
#include <vector>
using namespace std;
int main()
{
priority_queue<pair<int, int> > a;
//下面的写法就是小顶堆
//priority_queue<pair<int, int>,vector<pair<int, int> >,greater<pair<int, int> > > a;
pair<int, int> b(1, 2);
pair<int, int> c(1, 3);
pair<int, int> d(2, 5);
a.push(d);
a.push(c);
a.push(b);
while (!a.empty())
{
cout << a.top().first << ' ' << a.top().second << '\n';
a.pop();
}
}
//输出
2 5
1 3
1 2
9.3、自定义数据类型
运算符重载写法
#include <iostream>
#include <queue>
using namespace std;
//方法1
struct tmp1 //运算符重载<
{
int x;
tmp1(int a) {x = a;}//初始化
bool operator<(const tmp1& a) const
{
return x < a.x; //大顶堆
//return x>a,x 小顶堆
}
};
重写仿函数写法
//方法2
struct tmp2 //重写仿函数
{
bool operator() (tmp1 a, tmp1 b)
{
return a.x < b.x; //大顶堆
//return x>a,x 小顶堆
//下方这种写法也可以的,先找到x大的,如果x一样大找y小的。
//if(x!=a.x)return x<a.x; //大顶堆写法
//else return y>a.y; //小顶堆写法
}
};
int main()
{
tmp1 a(1);
tmp1 b(2);
tmp1 c(3);
priority_queue<tmp1> d;
d.push(b);
d.push(c);
d.push(a);
while (!d.empty())
{
cout << d.top().x << '\n';
d.pop();
}
cout << endl;
//注意写法 如果用了仿函数,那么三个参数是一定都要写的。
priority_queue<tmp1, vector<tmp1>, tmp2> f;
f.push(b);
f.push(c);
f.push(a);
while (!f.empty())
{
cout << f.top().x << '\n';
f.pop();
}
}
10、扩展
10.1、前序遍历和中序遍历构建树
给你一个树的前序遍历和中序遍历,你在建树的过程中就可以直接得到后序遍历的结果。 同理对于后序遍历和中序遍历,我猜是输出的前序遍历的结果,这个待考证,如果不放心还是DFS遍历一下吧
Node * create(int lp,int rp,int li,int ri){
if(lp>rp){
return NULL;
}
//int root=pre[lp];
Node * root=new Node;
root->val=pre[lp];
int k;
for(k=li;k<=ri;k++){
if(inorder[k]==pre[lp]){
break;
}
}
int numlen=k-li;
root->left=create(lp+1,lp+numlen,li,k-1);
root->right=create(lp+numlen+1,rp,k+1,ri);
//cout<<"root "<<root->val<<endl; 这里输出就是后序遍历的结果了
return root;
}