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;
}
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值