引言
牛客网刷题模式和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_MAX
和INT_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。