1. 输入多行字符串出错

1
2
3
4
5
6
7
8
9
    int n = 0;
cin >> n;
cin >> ws;
// 输入整数后,后面会跟着一个换行符,需要额外进行处理
for (int i = 0; i < n; i++) {
string line;
getline(cin, line, '\n');
}

关键:使用**cin>>ws**来吸收残存在输入流中的换行符

2. 判断字符串内容是否为整数

1
2
3
4
5
6
7
8
bool isNumber(const string &str) {
for (char const &c: str) {
if (isdigit(c) == 0) {
return false;
}
}
return true;
}

关键:调用**isdigit()**函数,一个一个进行判断,不要嫌弃麻烦
image.png
image.png

3. 字符串按字符切割

1
2
3
4
5
6
7
void stringSpilt(const string &str, const char split, vector<string> &res) {
istringstream iss(str);
string token;
while (getline(iss, token, split)) {
res.push_back(token);
}
}

关键:

  1. 调用**istringstream iss (str)**
  2. 调用 **getline(iss,token,split)**

4. 修改哈希表中的值

方法一

1
2
3
4
5
6
7
void setVariable(string &str, string &val) {
checkVal(val);
if (mapVal.count(str)){
mapVal.erase(str);
}
mapVal.insert({str, stoi(val)});
}

方法二

直接使用数组的表现形式进行增加或者修改

5. 字符串转数字

stoi

1
2
3
4
5
cout << stoi("2") << endl;   // 2
cout << stoi("21214 avb") << endl; //21214
cout << stoi("1000", nullptr, 2) << endl; //8
cout << stof("221.4240") << endl; //221.424
cout << stoi("221.4240") << endl; //221

6. 纯手工char数组转数字

写该函数时,涉及到的小的细节特别的多,需要条理清晰地进行解答。

  1. index问题,因为涉及到arrtmp双数组,且分别用了i j count三个数字来表示下标。因此,很容易出错。如果发现输入不同却输出相同时,很可能是下标**j**** 使用时不小心打的是****count**
  2. 初始化问题。因为传入的cmd数组初始化时是**char* cmd[20] = {'0'};** 。但是,最终赋值的时候,却初始化的是 **'\000'** ,对应的ASCII码值是0。而在这之中,我一直是以48的ASCII码值去进行计算。最终导致结果总是不对
  3. 下标和指数的不一致:该问题很经典。对于一个数组,读取时是需要从右往左读的,但是,这样子时,常用的**i**就不能直接作为pow的第二个参数使用,而是要额外使用一个变量。即:读取时是从右往左,计算时却是从左往右
  4. 最后的一个处理是char数组的结束符是跟随系统的变化而变化的,因此要十分小心。
  5. 关于负数🍀~蚌埠住了哇Σ(っ °Д °;)っ。还有小数的处理。要根据需要进行优化
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    int getNum(const char *cmd, int start) {
    char *tmp = (char *) malloc(sizeof(char) * DEF);
    for (int i = 0; i < DEF; ++i) {
    tmp[i] = '0';
    }
    int count = 0;
    for (int j = start; j < DEF; ++j) {
    char x = cmd[j];
    tmp[count] = cmd[j];
    count++;
    }
    int ans = 0;
    int w = 0;
    for (int i = count - 1; i >= 0; --i) {
    // int x = tmp[i]-'0';
    // x 被赋值为1 或者 -48 了 原因是cmd初始化时里面是 0'\000' 而不是 48'\0'
    // 两者的区别造成了算数时无法统一
    if (tmp[i] == 0) {
    continue;
    }
    // 最开始是使用tmp[count] OMG!!怎么会这么不小心
    ans += (tmp[i] - '0') * pow(10,w );
    w++;
    }
    free(tmp);
    return ans;
    }

7. 去重

set+assign

在三数求和为0的题目中,需要进行去重。那么最好的方式是使用set集合进行处理

1
2
3
set<vector<int>> tmp{ans.begin(), ans.end()};
ans.assign(tmp.begin(), tmp.end());
return ans;

此处ans中有重复的元素,则利用set<T>进行 初始化后再重新赋值即可

  • assign可以理解为利用迭代器重新进行赋值。那么其ans中的原本数据会丢失

    函数原型是:
    1:void assign(const_iterator first,const_iterator last);
    2:void assign(size_type n,const T& x = T());
    第一个相当于个拷贝函数,把first到last的值赋值给调用者;(注意区间的闭合-前闭后开
    第二个把n个x赋值给调用者;

sort+unique+erase

  1. unique函数属于STL中比较常用函数,它的功能是元素去重。即”删除”序列中所有相邻的重复元素(只保留一个)。此处的删除,并不是真的删除,而是指重复元素的位置被不重复的元素给占领了(详细情况,下面会讲)。由于它”删除”的是相邻的重复元素,所以在使用unique函数之前,一般都会将目标序列进行排序
  2. unique函数通常和erase函数一起使用,来达到删除重复元素的目的。(注:此处的删除是真正的删除,即从容器中去除重复的元素,容器的长度也发生了变换;而单纯的使用unique函数的话,容器的长度并没有发生变化,只是元素的位置发生了变化)
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    #include <iostream>
    #include <algorithm>
    #include <vector>

    using namespace std;
    int main()
    {
    int myints[] = {1,2,3,1,1};
    int len = sizeof(myints)/sizeof(int);
    vector<int> vec(myints, myints + len);
    sort(vec.begin(), vec.end());
    vec.erase(unique(vec.begin(), vec.end()), vec.end());
    for(int x : vec)
    cout << x << ",";
    return 0;
    }

    iterator unique(iterator it_1,iterator it_2);
    这种类型的unique函数是我们最常用的形式。其中这两个参数表示对容器中[it_1,it_2)范围的元素进行去重(注:区间是前闭后开,即不包含it_2所指的元素),返回值是一个迭代器,它指向的是去重后容器中不重复序列的最后一个元素的下一个元素。
    unique函数的去重过程实际上就是不停的把后面不重复的元素移到前面来,也可以说是用不重复的元素占领重复元素的位置。

8. 二分法

lower_bound

  1. 原型1:

    1
    2
    template <class ForwardIterator, class T>
    ForwardIterator lower_bound (ForwardIterator first, ForwardIterator last, const T& val);
  2. 原型2:

    1
    2
    template <class ForwardIterator, class T, class Compare>
    ForwardIterator lower_bound (ForwardIterator first, ForwardIterator last, const T& val, Compare comp);
模板参数解释
  1. ForwardIterator就是一个迭代器,vector< int > v,v数组的首元素就是 v.begin()
  2. T&val , 就是一个T类型的变量
  3. Compare 就是一个比较器,可以传仿函数对象,也可以传函数指针
函数作用:

前提是有序的情况下,lower_bound返回指向第一个值不小于val的位置,也就是返回第一个大于等于val值的位置。(通过二分查找)

参数、返回值含义
  • first,last: 迭代器在排序序列的起始位置和终止位置,使用的范围是[first,last)包括

firstlast位置中的所有元素

  • val: 在[first,last)下,也就是区分(找到大于等于val值的位置,返回其迭代器)
  • comp: 主要针对于原型二,传一个函数对象,或者函数指针,按照它的方式来比较
  • 返回值:返回一个迭代器,指向第一个大于等于val的位置
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    int main(){
    vector<int> v= {3,4,1,2,8};
    //先排序
    sort(v.begin(),v.end()); // 1 2 3 4 8
    // 定义两个迭代器变量
    vector<int>::iterator iter1;
    vector<int>::iterator iter2;
    iter1 = lower_bound(v.begin(),v.end(),3);//迭代器指向3
    iter2 = lower_bound(v.begin(),v.end(),8);//迭代器指向8(因为第一个大于等于8)
    auto iter3 = lower_bound(v.begin(),v.end(),10);
    cout << *iter1 << endl; //输出3
    cout << *iter2 << endl; //输出8 说明能够找到最后一个元素
    cout << *iter3 << endl; //输出为0
    cout << iter1 - v.begin() << endl; //下标 2
    cout << iter2 - v.begin() << endl; //下标 4
    cout << iter3 - v.begin() << endl; //下标5
    cout << v.end() - v.begin() << endl; //返回值为5
    system("pause");
    }

upper_bound

用法和上面类似。只是把lower_bound的大于等于换成大于。仿函数等等全是相同的用法

9. 迭代器

end()

容器的end()方法,返回一个迭代器,需要注意:这个迭代器不指向实际的元素,而是表示末端元素的下一个元素,这个迭代器起一个哨兵的作用,表示已经处理完所有的元素
因此,在查找的时候,返回的迭代器,不等于end(),说明找到了目标等于end(),说明检查了所有元素,没有找到目标。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class Solution {
public:
bool searchMatrix(vector<vector<int>>& matrix, int target) {
for (int i = 0; i < matrix.size(); i++)
{
auto it = lower_bound(matrix[i].begin(),matrix[i].end(),target);
if (it != matrix[i].end() && *it ==target)
{
// 此处用二分法获得迭代器,但同时使用 💥!=end() 来说明已经找到了
// 因为没找到都是默认返回end迭代器,或者说是最后一个元素的下一个元素
return true;
}

}
return false; }
};

10. 自定类处理

vector存储

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class movie {
public:
int movieId;
bool isRented;
int price;
int shopId;

movie(int movieId, int price, int shopId) {
this->movieId = movieId;
this->price = price;
this->shopId = shopId;
this->isRented = false;
}
};

对于movie类,如果要用vector存储,有以下可能的方式

1
2
3
4
5
6
vector<movie> movieList;
// 1.在vector中直接存储对象
vector<*movie> movieList;
// 2.错误的使用指针的表示方式
vector<movie*> movieList;
// 3.使用指向movie的指针

建议不直接存储对象

  1. STL的存储实际上是拷贝,因此会重新调用构造函数,再进行赋值,开销大
  2. STL中获得的值也不是原来的对象,而是一份新的拷贝,所以存储的指针的值也会改变
  3. 因此,使用指针能够节省成本

STL删除

1
2
3
4
5
6
7
list<obj *>m_list;
list<obj *>::iterator ite;
for( ite = m_list.begin(); ite != m_list.end(); ++ite)
{
delete (*ite);
ite = m_list.erase(ite);
}

delete,释放内存,在将其从容器中删除

错误

  1. shop *shop = new class shop_(_shopId_)_;

auto *ptr = new class shop_(_shop_)_;
如果只使用shop *shop;则在重新赋值的时候会出现错误

  1. 容器中最好存储指针,否则也会出现一些错误,使用指针在进行函数的调用和参数的传递时是比较方便的。

11. 自定义函数比较

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
vector<movie*> movieRented;
std::sort(this->movieRented.begin(), this->movieRented.end(), compMovie);
static bool compMovie(const movie *mov1, const movie *mov2) {
if (mov1->price == mov2->price) {
if (mov1->shopId == mov2->shopId) {
// * 5.如果仍然相同,则 moviej 较小 的排在前面
return mov1->movieId < mov2->movieId;
}
// * 4.如果价格相同,则 shopj 较小 的排在前面
return mov1->shopId < mov2->shopId;
}
// * 3.res 中的电影需要按 价格 升序排序

return mov1->price < mov2->price;
}
vector<vector<int>> target;
std::sort(target.begin(), target.end(), compShop);
static bool compShop(vector<int> x, vector<int> y) {
if (x[1] == y[1]) {
return x[0] < y[0];
}
return x[1] < y[1];
// * 2.商店需要按照 价格 升序排序
// * 3.如果价格相同,则 shopi 较小 的商店排在前面
}

12. 子串的寻找

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#include <iostream>
#include <string>
#include <cstring>
using namespace std;

int main() {
string s1, s2;
while(cin >> s1 >> s2) {
if(s1.size() >= s2.size())
cout << (s1.find(s2) != string::npos) << endl;
else
cout << (s2.find(s1) != string::npos) << endl;
}
return 0;
}

13. 得到子串

1
2
3
4
str1.substr(index, num)
// 从字符串str1的index索引位置开始获取num个字符;
str1.substr(index)
// 从字符串str1的index索引位置开始获取,一直获取到末尾的字符;

14. 字符串大小写转换

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#include <iostream>
#include <string>
using namespace std;

int main()
{
string s = "ABCDEFG";

for( int i = 0; i < s.size(); i++ )
{
s[i] = tolower(s[i]);
}

cout<<s<<endl;
return 0;
}

15. 字符串替换

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
1. string& replace(size_t pos, size_t n, const char *s);//将当前字符串

从pos索引开始的n个字符,替换成字符串s

2. string& replace(size_t pos, size_t n, size_t n1, char c); //将当前字符串从pos索引开始的n个字符,替换成n1个字符c

3. string& replace(iterator i1, iterator i2, const char* s);//将当前字符串[i1,i2)区间中的字符串替换为字符串s

void test7()
{
string s1("hello,world!");

cout<<s1.size()<<endl; // 结果:12
s1.replace(s1.size()-1,1,1,'.'); // 结果:hello,world.

// 这里的6表示下标 5表示长度
s1.replace(6,5,"girl"); // 结果:hello,girl.
// s1.begin(),s1.begin()+5 是左闭右开区间
s1.replace(s1.begin(),s1.begin()+5,"boy"); // 结果:boy,girl.
cout<<s1<<endl;
}

16. 字符串的构造函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
string str:生成空字符串

string s(str):生成字符串为str的复制品

string s(str, strbegin,strlen):将字符串str中从下标strbegin开始、长度为strlen的部分作为字符串初值

string s(cstr, char_len):以C_string类型cstr的前char_len个字符串作为字符串s的初值

string s(num ,c):生成num个c字符的字符串

string s(str, stridx):将字符串str中从下标stridx开始到字符串结束的位置作为字符串初值

eg:


string str1; //生成空字符串
string str2("123456789"); //生成"1234456789"的复制品
string str3("12345", 0, 3);//结果为"123"
string str4("012345", 5); //结果为"01234"
string str5(5, '1'); //结果为"11111"
string str6(str2, 2); //结果为"3456789"



1. size()和length():返回string对象的字符个数,他们执行效果相同。

2. max_size():返回string对象最多包含的字符数,超出会抛出length_error异常

3. capacity():重新分配内存之前,string对象能包含的最大字符数


17. 数字与字符串的相互转换

数字to字符串

方法一
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#include <iostream>
#include <sstream>
using namespace std;

int main()
{
double x;
string str;
stringstream ss;
cin >> x;
ss << x;
ss >> str;
cout << str;
return 0;
}

方法二
1
2
3
4
5
6
7
8
9
10
11
12
13
14
#include <iostream>
#include <sstream>
using namespace std;
// 浮点数会附带小数点后六位,不足补零,不推荐浮点数使用
int main()
{
double x;
string str;
cin >> x;
str = to_string(x);
cout << str;
return 0;
}

字符串to数字

方法一
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#include <iostream>
#include <string>
using namespace std;

// 利用<string>中的stoi()函数,其中还有对于其他类型的函数,如stod(),stof()等,根据类型选取
int main()
{
int x;
string str;
cin >> str;
x = stoi(str);
cout << x;
return 0;
}

方法二
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#include <iostream>
#include <sstream>
using namespace std;

int main()
{
double x;
string str;
stringstream ss;
cin >> str;
ss << str;
ss >> x;
cout << x;
return 0;
}

18. 溢出

1
2
3
    long long right = min(a,b)*(long long )n;
/// 如果不对n进行强制类型转换,那么溢出。
// 虽然right已经定义了long long,但是在乘法计算时就已经溢出了

19. map遍历

1
2
3
4
5
6
7
8
9
map<int,int>_map;
map<int,int>::iterator iter;
while(iter!=_map.end()){
cout << iter->first << ":" << iter->second<<endl;
iter++;
}
for(iter = _map.begin(); iter != _map.end(); iter++) {
cout << iter->first << " : " << iter->second << endl;
}