C++牛客网非核心代码模式

引言

牛客网刷题模式和leetcode的核心代码模式不同,其需要自行处理输入输出。本篇介绍刷题如何处理输入输出,以及刷题遇到的一些常见函数与其他知识。

以之前遇到的华为od的一道真题为例,处理输入输出:
二叉树层序遍历
题目描述
有一棵二叉树,每个节点由一个大写字母标识(最多26个节点)。
现有两组字母,分别表示后序遍历(左孩子->右孩子->父节点)和中序遍历(左孩子->父节点->右孩子)的结果,请你输出层序遍历的结果。

输入
每个输入文件一行,第一个字符串表示后序遍历结果,第二个字符串表示中序遍历结果。(每串只包含大写字母)
中间用单空格分隔。

输出
输出仅一行,表示层序遍历的结果,结尾换行。

示例1 输入输出示例仅供调试,后台判题数据一般不包含示例
输入
CBEFDA CBAEDF
输出
ABDCEF

#include<iostream>
#include<string>
#include<vector>
#include<stack>
#include<queue>
using namespace std;

struct Node
{
	Node *left;
	Node *right;
	int val;
	Node(int x) :val(x), left(nullptr), right(nullptr) {}
};

void SplitString(string input, vector<string> &ouput, string pattern)
{
	string::size_type pos;
	input += pattern;
	for (int i = 0; i < input.size(); i++)
	{
		pos = input.find(pattern, i);
		if (pos < input.size())
		{
			string temp = input.substr(i, pos - i);
			if (!(temp.empty()) && (temp != pattern))
			{
				ouput.push_back(temp);
			}
		}
		i = pos + pattern.size() - 1;
	}
}

Node *builTree(string backArray, string medArray)
{
	//如果后序序列为空(因为在不断删减)
	if (backArray.empty())
		return nullptr;
	//拿到后序的最后节点为根节点
	int head = backArray.back();
	//建立当前树的根节点
	Node *root = new Node(head);

	//在中序序列中找到该根节点的位置,进行分割左右
	int headIndex = medArray.find(head);
	//当根节点位置让左有节点,创建左子树(左子树长度都一样长)
	if (headIndex > 0)
	{
		root->left = builTree(backArray.substr(0, headIndex), medArray.substr(0, headIndex));
	}
	else
	{
		root->left = nullptr;
	}
	//如果有右子树
	if (headIndex < medArray.size() - 1)
	{						//注意每次后序序列都要除去尾节点(根节点),这里易错
		root->right = builTree(backArray.substr(headIndex).erase(backArray.substr(headIndex).size() - 1), medArray.substr(headIndex + 1));
	}
	else
	{
		root->right = nullptr;
	}

	return root;
}

int main()
{
	string s;
	getline(cin, s);
	//分割为后序中序遍历序列
	vector<string> treeArrays;
	SplitString(s, treeArrays, " ");
	//拿到后序中序序列
	string a = treeArrays[0];
	string b = treeArrays[1];

	//根据后序和中序序列构造出树
	Node *node = builTree(a,b);
	
	//进行层序遍历
	string tree;
	queue<Node *> Q;
	Q.push(node);
	while (!Q.empty())
	{
		Node *head = Q.front();
		Q.pop();
		tree.push_back(head->val);
		if (head->left != nullptr)	Q.push(head->left);
		if (head->right != nullptr)	Q.push(head->right);
	}
	cout << tree << endl;

	return 0;
}

1.字符串分割的几种常见处理方式

(1)输入输出

首先先回顾一下输出输出:
C++的输入输出通常使用cin、cout对象加上<< >>输入输出运算符。

①cin

cin是以空格、换行符为界(即不读入)来输入内容。
循环读入内容,并遇到换行符退出:

while (cin >> num) {
		nums.push_back(num);
		if (getchar() == '\n') break;
	}

②getline

cin每次遇到空格和换行符就会结束,那么如果不想结束,就要使用getline
getline能够读入空格与换行符,并且默认是以换行符结束的,所以这里存在一个陷阱:

如果在getline之前用了cin或getline,那么cin之后的输入的换行符会被getline读取,导致真正的内容无法读入。

getline(cin,str,a)是将str读入到cin流中,当a不写时默认遇到换行符停止输入,并将换行符丢弃,如果a写了,如a为‘#’则表示遇到‘#’停止读入。

while(getline(cin,line))语句注意这里默认回车符停止读入,按Ctrl+Z或键入EOF回车即可退出循环。在这个语句中,首先getline从标准输入设备上读入字符,然后返回给输入流cin,注意了,是cin,所以while判断语句的真实判断对象是cin,也就是判断当前是否存在有效的输入流。

原理:

cin.getline读取换行符并替换成’\0’,并且不会主动丢弃换行符,会把它留在输入队列中。

使用:
循环读入每一行,遇到空行结束

while (getline(cin, line) && line.compare("") != 0) {	//compare相等返回0,>返回1,<返回-1
		line += '\n';			//每输入完一行都换行
		file += line;			//将line放入file中
		line.clear();			//清空line为下次做准备
	}

解决方案就是清空输入缓冲区

③关于清空输入缓冲区

先补充一下C语言的输入
scanf:不会读入空格,Tab,换行符,遇到这些会输入结束。
gets:可以读入空格,Tab,换行符。
两者在读取字符串结束后会在末尾加’\0’

关于清空输入缓冲区,一般使用cin.ignore();
容易混淆的点:

cin.clear();是清理错误标识符而不是清空缓冲区。
当输入的类型不符合定义的变量类型时,会出现输入异常的情况 if (cin.fail()),那么如果输入错误,就要使用cin.clear()清理错误标识符,然后使用cin.ignore()取走刚才流中的字符来实现清空输入缓冲区。
感兴趣的可以使用cin.rdstate()来输出错误标识符
在刷题时,一般使用cin.ignore()来防止getline读入换行符即可。

(2)利用 istringstream类 对字符串进行以空格为分割的操作

istringstream类用于执行C++风格的串流的输入操作。是从string对象str中读取字符碰到空格隔断,又读下一个。即将从str中读取到的字符串(is) 写入到字符串s
这里其实可以不止用以空格为分割,其他字符也可以。
具体:

string sOperation;
getline(cin, sOperation);

//istringstream input;	
//input.str(sOperation);
//或者
istringstream input(sOperation);

//将input以空格分割后每个放入string p中,再放入vsOperation中
vector<string> vsOperation;
for (string p; getline(input, p, ' ');)
	vsOperation.emplace_back(p.c_str());

(3)实现函数来对字符串用字符/字符串分割为数组

以下四种方法大同小异,其实记住一种就可

①输入字符串用字符分割为整型数组

void SplitInt(string input, vector<int> &output, char patten) {
	int j = 0;
	input += patten;
	for (int i = 0; i < input.size(); i++) {
		if (input[i] == patten) {
			string one_word = input.substr(j, i - j);
			if (!one_word.empty()) {
				output.push_back(stoi(one_word));
			}
			j = i + 1;
		}
	}
}

②输入字符串用字符串分割为整型数组

void Split(string input, vector<int> &output, string pattern)
{
	string::size_type pos;
	input += pattern;
	for (int i = 0; i < input.size(); i++) {
		pos = input.find(pattern, i);
		if (pos < input.size()) {
			string temp = input.substr(i, pos - i);
			if ((temp != pattern) && (!temp.empty())) {
				output.push_back(stoi(temp));
			}
			i = pos + pattern.size() - 1;
		}
	}
}

③输入字符串用字符分割为字符串数组

void Split(string input, vector<string> &output, char patten) {
	int j = 0;
	input += patten;
	for (int i = 0; i < input.size(); i++) {
		if (input[i] == patten) {
			string one_word = input.substr(j, i - j);
			if (!one_word.empty()) {
				output.push_back(one_word);
			}
			j = i + 1;
		}
	}
}

④输入字符串用字符串分割为字符串数组

void SplitString(string input, vector<string> &output, const string &pattern)
	{
		string::size_type pos;
		input += pattern;
		for (int i = 0; i < input.size(); i++) {
			pos = input.find(pattern, i);
			if (pos < input.size()) {
				string temp = input.substr(i, pos - i);
				if ((temp != pattern) && (!temp.empty())) {
					output.push_back(temp);
				}
				i = pos + pattern.size() - 1;
			}
		}
	}

2.易混淆的容器方法

这里只介绍刷题经常用到的容器及其方法,详细的可以去看另一篇C++STL详述

(1)string的插入方法

string中的push_back()和append()函数都是向string的结尾插入,但push_bach()只能插入单个字符char,而append可以插入string。

(2)删除特定元素的方法

对于连续内存容器(vector,deque,string)可以使用erase-remove方法。
erase方法可以删除vector容器中的一个或者一段元素,注意没有改变空间capacity的大小
remove方法可以删除匹配特定值的一段元素,删除挪动完元素的剩余空间是空字符串。

想要删除容器中的特定值,并且又不想要空字符串,那么就用erase-remove方法

words.erase(std::remove(words.begin(),words.end(),"none"),words.end());

总结:
也就是说erase和remove的区别在于执行函数之后返回值不同,被执行的vector的大小会不会改变
vector中erase的作用是删除掉某个位置position或一段区域(begin, end)中的元素,减少其size,返回被删除元素下一个元素的位置。
vector中remove的作用是将范围内为val的值都remove到后面,返回新的end()值(非val部分的end),但传入的原vector的end并没有发生改变,因此size也就没有变化

(3)map实现键值对的互换

	map<char, int> hashTable;
	map<int, char> reversedMap;
	for (auto it : hashTable)
			reversedMap.insert(pair<int, char>(it.second, it.first));
	}

(4)map、multimap、unordered_map

map: map内部实现了一个红黑树,该结构具有自动排序的功能,因此map内部的所有元素都是有序的,红黑树的每一个节点都代表着map的一个元素,因此,对于map进行的查找,删除,添加等一系列的操作都相当于是对红黑树进行这样的操作,故红黑树的效率决定了map的效率。

unordered_map: unordered_map内部实现了一个哈希表,因此其元素的排列顺序是杂乱的,无序的。

查询效率:unordered_map 查询复杂度O(1), map查询复杂度O(logn)

运行效率方面:unordered_map最高,而map效率较低但 提供了稳定效率和有序的序列。

占用内存方面:map内存占用略低,unordered_map内存占用略高,而且是线性成比例的。
在这里插入图片描述

(5)set、multiset、unordered_set

在这里插入图片描述
通常用来对vector数组进行去重和排序

//用set来记录所有牌面,这样能去重并且排序
set<int>st(all_poker.begin(), all_poker.end());
//将set记录的剩余牌放入整型数组中(已去重)
vector<int> remain_poker(st.begin(), st.end());

3.常用的库函数

(1)sort函数自定义排序

系统自带的sort排序是快速排序
first是元素的起始地址,last是结束地址,cmp是排序的方式。对[first,last)(一定要注意这里的区间是左闭又开)

1.对整型进行默认升序排序

int a[10] = {2, 3, 30, 305, 32, 334, 40, 47, 5, 1};
vector<int> nums(a, a + 10);
sort(nums.begin(), nums.end());

//或者
sort(a,a+10);

---------------------------------------------
//或者降序排序
bool cmp(const int& a, const int& b)
{
	return a > b; //从大到小排序
}
sort(nums.begin(), nums.end(), cmp);

2.对字符串进行排序(字典序排序,默认升序)

sort(str.begin(),str.end());

3.自定义排序

①比如,对数组进行排降序
sort(a,a+10,cmp);

bool cmp(int a,int b)

{	return a>b;	}

对 x 进行排降序,在 x 相等的情况下,我们对 y 进行排升序

②当出现结构体的时候,有多个参数进行排序,可以直接在结构体里面进行cmp比较,或者用外部函数进行比较

用外部函数:

struct node
{
	int x;
	int y;
}
bool cmp(node a,node b)
{
	if(a.x!=b.x)	return a.x>b.x;		//前面的数大于后面的数(降序)
	return a.y<b.y;						//如果x相等,那么对y进行排升序
}

node a[100];
sort(a,a+n,cmp);

用成员函数运算符重载:

struct node
{
	int x;
	int y;
    //因为sort内部就是快排,内部用小于号时进行重载
	bool operator<(const node& b) const
	{
		if(x!=b.x) return x>b.x;	//前面的数x大于后面的数b.x(降序)
		return y<b.y;				//如果x相等,那么对y进行排升序
	}
}
sort(a,a+10);
③直接写在sort库函数排序
vector<pair<int, int>> end;
//放入次数和元素
for (int x : ACE) end.emplace_back(cnt[x], x);
//对end进行排序
sort(end.begin(), end.end(), [](pair<int, int> &lhs, pair<int, int> &rhs) {
			return lhs.first > rhs.first;	//左边的数大于右边的数,即降序排列
});
④functional提供了一堆基于模板的比较函数对象
升序:sort(begin,end,less<data-type>());
降序:sort(begin,end,greater<data-type>();

(2)count(v.begin(), v.end(), key) 函数

对于只需要知道包含特定元素的数量的应用来说,这是最简单的方式。如果count返回0,则表示不存在该元素。

vector<int> v{ 4, 7, 9, 1, 2, 5 };
int key = 2;
if (count(v.begin(), v.end(), key)){
	cout << "Element found" << endl;
}

(3)find(v.begin(), v.end(), key) 函数

find会在查找到指定值后立即返回,所以它一般比count更快(因为count总是要遍历整个容器)

 std::string const s = "This is a string";
  // 从 string 开始搜索
  n = s.find("is");
  // 从位置 5 开始搜索
  n = s.find("is", 5);
  // 寻找单个字符
  n = s.find('a');
//如果没有找到当前字符
find(List.begin(), List.end(), count[i][0]) == List.end()

(4)find_if(v.begin(), v.end(), [] () { } )

find_if需要一个判别式。如果查找的值需要满足特定的条件时,比如查找小于3且大于1的值时,适合该方式。
如果有多个值符合条件,则返回查找到符合条件的第一个值的迭代器。

if (std::find_if(v.begin(), v.end(), [] (int i) { return i < 3 && i > 1 } ) != v.end())

(5)for_each() 修改区间内元素

头文件:#include
常用用法: for_each(v.begin(), v.end(), 全局函数名);
for_each() 函数,允许对区间内的元素进行修改,当然transform也可以实现相同的操作,只是transform效率较低,因为transform是通过拷贝函数返回值实现。 当然 C++ 11之后 for_each 变得不再重要,因为range-based for更加直观简单。这里仅作为了解。
for_each()事实上是個function template,其实质如下[effective STL item 41],for_each()只能配合global function和function object。

经常和 vector 容器搭配使用,在vector容器中 for_each 遍历算法如下:
void MyPrint(int val){
		cout << val << endl;
	}
for_each(v.begin(), v.end(), MyPrint);	

vector<string> result;
for_each(all(result), [&](const string &res) 
{
		cout << res << endl;
}

(5)reverse函数

多用于字符串、数组、容器。reverse函数用于反转在 [first,last) 范围内的顺序(包括first指向的元素,不包括last指向的元素),reverse函数无返回值。

string str="hello world , hi";
reverse(str.begin(),str.end());//str结果为 ih , dlrow olleh

vector<int> v = {5,4,3,2,1};
reverse(v.begin(),v.end());//容器v的值变为1,2,3,4,5

(6)max_element()、 max({a, b, c}) 找最大值

max_element() 找有前向迭代器序列区间中的最大值,如数组、列表等。返回 [ 前闭后开)序列区间中指向最大值的迭代器,输出值的话要在max_element前面加星号,第三个参数cmp可写可不写。

max_element() 和 min_element() 默认是从小到大排列,然后
max_element() 输出最后一个值min_element() 输出第一个值。如果自定义的 cmp函数写的是从大到小排列,那么会导致 max_element() 和min_element() 的两个结果是对调的。

int a[5] = {0, 3, 9, 4, 5};
int *b;
b = max_element(a, a+5);
cout << *b;

//也适用于向量
vector<int> myvector;
vector<int>::iterator it;
it = max_element(myvector.begin(),myvector.end());
cout<<*max_element(myvector.begin(),myvector.end());

//或者自己写一个比较函数,作为第三个参数
static bool cmp(int& a, int& b)
{
	return abs(a)<abs(b);
}
it = max_element(myvector.begin(),myvector.end(),cmp);	
cout<<*it<<end;

max({a, b, c}) 利用 initializer_list 来找多个元素中的最大值
在C++11中有了initializer_list(初始化列表)后,max函数可以传递更多的参数,如下:

cout << max({ 54,16,48,5 }) << endl;   //输出54

【注意】max 函数中的参数类型需要一致,不一致时需要进行类型转换,如下,size()函数返回值为 unsigned int 类型,不是int类型,类型不一致时会报错!

int maxlen = 0;

//word.size()不是int,max 函数需要参数类型统一
maxlen = max(maxlen, (int)word.size()); 

(7)取整函数 ceil、floor和round

1、ceil的英文意义是天花板,该方法表示向上取整
2、floor的英文是地板,该方法就表示向下取整
3、round方法表示“四舍五入”,算法为floor(x+0.5),即将原来的数字加上0.5后再向下取整,等价于(int)(m +0.5)。

(8)INT_MAX/MIN 最大最小整型

C++中常量INT_MAXINT_MIN分别表示最大、最小整数。
头文件#include<climits>

(9)next_permutation(全排列算法)

next_permutation()会取得[first,last)所标示之序列的下一个排列组合,如果没有下一个排列组合,便返回false;否则返回true。
算法思想:
1.首先从最尾端开始往前寻找两个相邻元素,令第一元素为i,第二元素为ii,且满足i<ii。
2.找到这样一组相邻元素后,再从最尾端开始往前检验,找出第一个大于
i的元素,令为
j,将i,j元素对调(swap)。
3.再将ii之后的所有元素颠倒(reverse)排序。

//对整型数组进行全排列,直到没有全排列序列了,没有了hasNextPermutation为false
while (hasNextPermutation) 
	hasNextPermutation = next_permutation(vec.begin(), vec.end());

next_permutation(num,num+n)函数是对数组num中的前n个元素进行全排列,同时并改变num数组的值。
next_permutation()在使用前需要对欲排列数组按升序排序,否则只能找出该序列之后的全排列数
next_permutation(node,node+n,cmp)可以对结构体num按照自定义的排序方式cmp进行排序。
可以对int、char、string类型进行全排列

vector<string> nums(n);
sort(nums.begin(), nums.end());
do {
		string StringBuilder;
		//就是交给next_permutation进行nums的全排列后进行输出
		for (int i = 0; i < n; i++)
			StringBuilder.append(nums[i]);
		result.push_back(StringBuilder);
	} while (next_permutation(nums.begin(), nums.end()));

注:使用 next_permutation(首地址,尾地址) 进行全排列的数列必须是以最小字典排列(即由小到大排列)开始的。
prev_permutation()函数则要求原数列必须由大到小进行排列。
因此这两个库函数一般与sort()函数结合使用

vector<vector<int>> permute(vector<int>& nums) {
        vector<vector<int> > ans;
        sort(nums.begin(),nums.end());//从小到大排列
    
    	//需要注意的是,全排列从当前数组的下一个排列开始,当前数组需要提前输出并保存。
        ans.emplace_back(nums);
        while(next_permutation(nums.begin(),nums.end()))
        {
            ans.emplace_back(nums);
        }
        return ans;
    }

需要注意的是,全排列从当前数组的下一个排列开始,当前数组需要提前输出并保存。

(10)accumulate() 累加函数

头文件#include<numeric>
将一段数字从头到尾累加起来,或者使用指定的运算符进行运算。
accumulate(first, last, 初值,操作);四个参数:累加的元素起始地址;累加的元素结束地址,累加的初值(通常为0);第四个参数为进行的操作,默认为累加。

求和样例:

求和时,accumulate带有三个形参:累加的元素起始地址;累加的元素结束地址,累加的初值(通常为0)。

#include<iostream>
#include<vector>
#include<numeric>
using namespace std;

int main() {
    vector<int> nums = {1, 2, 3, 4, 5};
    int result = accumulate(nums.begin(), nums.end(), 0);
    cout << result << endl; //result = 15    
    return 0;
}

求连乘积时,accumulate带有四个形参:连乘的元素起始地址;连乘的元素结束地址,连乘的初值(通常为1), multiplies<int>() 运算

con_product= accumulate(list, list+3, 1, multiplies<int>()) ;//得出sum=6.

string合并

vector<string>a{"1","-2345","+6"};
string a_sum=accumulate(a.begin(), a.end(),string("out: "));//得到out: 1-2345+6

(11)字母转换

tolower() 大写字母转小写

toupper() 小写转大写

transform(first,last,result,op) 操作作用每个元素
将某操作应用于指定范围的每个元素。
比较常用的重载函数版本是:transform(first,last,result,op);
其中,first是容器的首迭代器,last为容器的末迭代器,result为存放结果的容器,**op为要进行操作的一元函数对象(比如::toupper或::tolower)**或sturct、class。

利用transform函数将一个给定的字符串中的小写字母改写成大写字母,并将结果保存在一个叫second的数组里,原字符串内容不变。

#include <iostream>
#include <algorithm>
using namespace std;
char op(char ch)
{
    if(ch>='A'&&ch<='Z')
        return ch+32;
    else
        return ch;
}
int main()
{
    string first,second;
    cin>>first;
    second.resize(first.size());
    
    transform(first.begin(),first.end(),second.begin(),op);
    
    cout<<second<<endl;
    return 0;
}

当然可以用已有函数对象(比如::toupper或::tolower)进行简写,并将结果保存至str字符串自身中。

string str;  cin>>str;
transform(str.begin(), str.end(), str.begin(), ::toupper);//转为大写
transform(str.begin(), str.end(), str.begin(), ::tolower);//转为小写

isalpha() 判断是否是字母
isdigit() 判断是否十进制数
isalnum() 判断是否字母或数字
islower() 判断是否小写字母
isupper() 判断是否大写字母
如果不是,这些函数返回值都是0

(12)transform()

算法transform()提供以下两种能力:

1.第一形式有4个参数,把源区间的元素转换到目标区间。也就是说,复制和修改元素一气呵成;
2.第二形式有5个参数,将前两个源序列中的元素合并,并将结果写入目标区间

注意:含有修改元素,仅仅只做复制元素,可以使用copy()。

第一种形式

transform(sourceBeg,sourceEnd,destBeg,op)

(1)针对源区间[sourceBeg,sourceEnd)中的每一个元素调用op(elem) 并将结果写到以destBeg起始的目标区间内

(2)返回目标区间内“最后一个被转换元素”的下一个位置,也就是第一个未被覆盖的元素位置;

(3)调用者必须确保目标区间有足够的空间,要不就得使用插入型迭代器;

(4)sourceBeg于destBeg可以相同,所以,和for_each()算法一样,你可以使用这个算法来变动某一序列内的元素;

(5)如果想以某值替换符合某一准则的元素,应使用replace()算法;

(6)复杂度:线性;

transform(all(eachStr), back_inserter(dstTemp), ::toupper);

第二种形式

transform(source1Beg,source1End,source2Beg,destBeg,op)

(1)针对第一源区间[source1Beg,source1End)以及“从source2Beg开始的第二源区间”的对应元素,调用:op(source1Elem,source2Elem) 并将结果写入以destBeg起始的目标区间内;

(2)返回区间内的“最后一个被转换元素”的下一位置,就是第一个未被覆盖的元素的位置;

(3)调用者必须保证第二源区间有足够空间(至少拥有和第一区间相同的空间大小);

(4)调用者必须确保目标区间有足够空间要不就得使用插入型迭代器

(5)source1Beg,source2Beg,destBeg可以相同。所以,可以让元素自己和自己结合,然后将结果覆盖至某个序列;

(6)复杂度:线性;

(13)插入迭代器

C++标准模板库还定义了几种特殊的迭代器,分别是插入迭代器、流迭代器、反向迭代器和移动迭代器,定义在<iterator>头文件

插入迭代器是指被绑定在一个容器上,可用来向容器插入元素的迭代器。
back_inserter:创建一个使用push_back的迭代器
inserter:此函数接受第二个参数,这个参数必须是一个指向给定容器的迭代器。元素将被插入到给定迭代器所表示的元素之前。
front_inserter:创建一个使用push_front的迭代器(元素总是插入到容器第一个元素之前)
由于list容器类型是双向链表,支持push_front和push_back操作,因此选择list类型来试验这三个迭代器。

list<int> lst = { 1, 2, 3, 4, 5, 6, 7, 8, 9 };
list<int> lst2 ={10}, lst3={10},lst4={10};

copy(lst.cbegin(), lst.cend(), back_inserter(lst2));
//lst2包含10,1,2,3,4,5,6,7,8,9
//该函数就复制了lst中的全部元素并将它们添加到lst2的末尾,执行完该函数后,lst2的长度将增加in.size()。

copy(lst.cbegin(), lst.cend(), inserter(lst3, lst3.begin()));
//lst3包含1,2,3,4,5,6,7,8,9,10

copy(lst.cbegin(), lst.cend(), front_inserter(lst4));
//lst4包含9,8,7,6,5,4,3,2,1,10

如果不使用std::back_inserter,则要保证目标容器的大小不小于源容器;

(14)compare函数

C++string的compare()比较函数
两个字符串相同,返回0。

调用字符串小与被调用字符串,返回-1。
调用字符串大于被调用字符串,返回1。
字符串说的大小通常和字典顺序是一致的。 字符串小的在字典里靠前,字符串大的在字典里靠后。即返回值是-1的话,调用字符串比被调用字符串靠前;返回值是1的话,调用字符串比被调用字符串靠后。

compare()比较时逐字符比较的,一旦能比较出结果,就不再比较了。 例如“abc”和“adf”,首先a和a比较,比不出结果;则b和d比较,结果就是“abc”小于“adf”,返回-1,即字典里“abc”在“adf”前面。例如“abc”和“abcd”比较,若“abc”都比完了,“abcd”还没完,说明“abc”小,返回值为-1,字典里“abc”靠前。总之记住这个比较规则和字典顺序一致即可。

4.其他常考点

(1)利用 / %

在这里插入图片描述

(2)类型转换

itoa

itoa是广泛使用的非标准C语言和C++语言扩展功能。但因为它是一个非标准的C / C++语言功能,因此不能好好的被所有编译器使用。在大多数Windows下的编译器通常在头文件包含非标准函数。

atoi

但是atoi一般可以,头文件是stdlib,意思是字符串(char*)转整型

atoi等:不接受string作为输入参数,需将string转化为char*。同时,atoi不进行范围检查,超出类型上/下界时直接输出上/下界。

不适用于string类string类型需要先用c_str()函数进行转换再用atoi()函数。

atoi()会扫描参数string字符串,跳过前面的空格字符串,直到遇上数字或正负号才开始做转换,而再遇到非数字或字符串‘\0’时才结束转换,并将结果返回,返回转换后的整型数。

c_str()函数返回一个指向正规c字符串的指针,内容与string串相同。将string对象转换为C中的字符串样式。

	std::string str="123";
	int a =str-'0'; //错误,不可以直接用一个字符串减去‘0’
	int n = atoi(*str);  //错误
	int n = atoi(str.c_str());   //正确,可以将string转为int

stoi

将字符串转换为整型

stoi(字符串,起始位置,n进制),将 n 进制的字符串转化为十进制
stoi(str, 0, 2); //将字符串 str 从 0 位置开始到末尾的 2 进制转换为十进制

stoll()

将字符串转换为long long

总结

atoi()的参数是 const char* ,因此对于一个字符串str我们必须调用 c_str()的方法把这个string转换成 const char类型的,而stoi()的参数是const string,不需要转化为 const char*;

stoi函数会做范围检查,所以数字的类型不能超过int范围,不然会报错,而且在转换的过程中会发生int强制类型转换,所以当有小数点或题目输入字符串长度大于等于10位时,一定要注意!
一个可行的解决方法是使用stoll函数代替stoi,将string转化为long long int。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值