文章目录
STL学习指南
STL 指的就是C++的 标准模板库(Stand Template Library),所谓模板,是指不必预先制定类型的函数或类,STL 为用户提供了多种名为容器( Container ) 的类包括 动态数组 、 表 、 栈 、 队列等数据结构,可以说它是我们平时刷题的利器,本篇针对于算法刷题,总结自己常用到的一些简单的使用教程以及和 使用C的不同之处,由C到C++的一个完整的过渡。主要是针对学完C想使用C++的小伙伴~省的去看一堆厚厚的,而且都写的都是关于C++特性的东西,顺便自己做一个总结。
由C到C++过渡
由C使用到C++我们需要了解,C与C++有什么区别, C++中有什么好用的特性,下面一一具体说到
头文件
从使用C到C++的这个过渡,那么我们就从程序的最起始的部分开始,看看C和C++区别在那里 以一个简短的a+b问题为例:
#include <cstdio>
int main(){
int a, b;
while(scanf("%d%d", &a, &b) == 2) printf("%d\n", a + b);
return 0;
}
??? 这么一看? 貌似没什么区别呀? 注意头文件,之前在C中使用的头文件是
stdio.h
,而现在换成了stdio
,这就是使用C++的区别之一,实际上stdio.h是依旧存在,但是在C++中推荐是用的是cstdio
,同理,string.h
就变成了cstring
、math.h
变成了cmath
、ctype.h
变成了cctype
, 所用以后使用原来C的头文件的换成一下的写法:
#include <cstdio>
#include <cmath>
#include <cstring>
#include <cctype>
C++中的特有的输入输出
在C++简化了输入输出的形式 ,不用去考虑输入输出的信息的类型 ,我把刚刚 的那个程序用C++的格式写一下看看区别
#include <iostream>
using namespace std;
int main(){
int a, b;
while(cin >> a >> b) cout << a + b << endl;
return 0;
}
这样一看,代码清爽了许多,但是可能也有不熟悉C++的小伙伴不太清楚,
#include <iostream>
,using namespace std
是个啥?iostream
用中文解释就是输入输出流(output input stream) 对应了C中的stdio.h
标准输入输出(Standard input output) ,其实他们就是同样的意思,用来做输入输出的,using namespace std
留到一会再详细解释 ~
scanf
、printf
在stdio.h
中,cin
、cout
在iostream
中,cin
和cout
简化了scanf
、printf
输入输出方式,cin >> a
对应了scanf("%d", &a)
,cout << a
对应了printf("%d", a)
, 而且使用cin
、cout
不用像在C中那样去写出对应输入输出%d
、%f
、%c
这样的数据的类型。输出时的
endl
表示此行结束(end of line),其实就是换行和C中\n
效果一样 为了简洁一点出现变量时使用cout << a << endl
,字符串就是使用cout << " hello\n"
注意 :
cin
、cout
的速度是比scanf
、printf
慢的,具体想了解的话可以看看这道题就能体现的出来 ,在某些题上的特定情况会出现超时的现象- 使用C++以后,并不是使用
cin
、cout
一直都很好,这个要根据具体的情况决定,如果有的题目需要输出带几位特定小数的的情况,此时cout
貌似就没有那么好用啦~例如某个数字前特定的有几个零时printf("%04d",15)
, 或者输出有多个空格等其他特殊格式时,也使用printf
解释using namespace std
其实翻译过来就是使用名称空间
std
,因为在C++中都会出现cin
、cout
这样的语句 输入,输出的本身的写法的是std::cin
、std::cout
如果的函数有多个输入输出的话这样写就非常繁琐,所有我们会加上using namespace std
来去简化写法,当然啦~ 你不加上那句话可以的,那天它就是这个样子的~
#include <iostream>
int main(){
int a, b;
while(std::cin >> a >> b) std::cout << a + b << std::endl;
return 0;
}
C++中特有的bool变量
C++中有出现了一个C中没有的变量
bool
它叫做布尔,其实就是和java
中 的boolean
是一个东西啦~ 它只有两个值——true
、flase
,不同的就是bool
也可以用数字来表示,非零的数字表示true
,零表示false
。
C++中特有的string
string的出现可以说是大大简化了输出字符的过程,在C中只有
char
类型的数据,如果你想使用输出string这样的格式,那么你就是得使用指针(char *)或者二维数组表示,非常麻烦, 我们看看在C++中使用的吧~
string的赋值
string的赋值其实和其他的变量赋值没有什么区别,但是它也可是使用构造的方式给他赋值(虽然这样的方法用的少)
string s ("hello"), s1 = " hello~~";
cout << s << s1;
string的拼接
//直接使用 + 就可以拼接字符串非常简洁
string s1 = "hello", s2 = "world";
string s = s1 + s2;
cout << s;
string的长度
//通过size() 和length()都能获取长度
string s1 = "hello", s2 = "world";
string s = s1 + s2;
cout << s.size() << s.length();
string的比较
一般情况下:
/**
string的比较是使用 >, <, <=, >=, ==, != 这样的符号来表示的
string通过字典的顺序从前到后比较,遇到不同的字符比较字典序,
字典序靠前的的字符小,靠后的就是字符大(理解为按照ASCII),出现不同时通过本次的不同比 较出当前字符串的大小
**/
string s1 = "ab", s2 = "aca";
if(s1 > s2) cout << s1;
else cout << s2; //明显的输出的就是aca
出现字符一致长度不同时
string s1 = "aa", s2 = "aaa";
if(s1 > s2) cout << s1;
else cout << s2; //输出aaa
出现大小写时
//出现大小写通过ASCII比较,A的ACSII码为65 a为97
string s1 = "Acz", s2 = "acZ";
if(s1 > s2) cout << s1;
else cout << s2; //输出acZ
string字符串的比较用的还是比较多的,例如如果出现学生的成绩相同, 通过他们的名称自典顺序比较~~,以后刷题上会遇到类似的题目。
string的子串
在处理 字符串的时候常常会用到字符串的子串,下面看看字符串的子串是怎么使用的吧
截取子串的一般是有两种用法
string t, s = "hello!";
//截取字符串的下标为0开始,2个字符
t = s.substr(0,2);
cout << t << endl; // he
//下标为1到字符串尾
t = s.sbustr(1);
cout << t << endl; // ello!
string的大小写转换
C++中大小写通过
tolower
,toupper
方式转化大小写,目前提供一种基本的方式,其他方法后续提到。
#include <iostream>
#include <cstdio>
using namespace std;
int main(){
string s = "hello";
for(int i = 0; i < s.size(); i++){
//转化为大写 对于单个字符
s[i] = toupper(s[i]);
//转化为小写
s[i] = tolower(s[i]);
}
cout << s;
return 0;
}
string的输入
使用输入时大概三种情况
-
使用
cin
/*对于不同的情况输入字符,使用`cin`的方式中间出现空格就会分隔 ,即空格为分隔符,前后表示 s1, s2 */ string s1, s2; cin >> s1 >> s2; cout << s1 << s2;
-
使用
getline
/*对于某种特殊的情况你也可能使用读入一整行的字符,这个方法通`getline`实现*/ string s; getline(cin, s); cout << s;
-
同时使用时
注意:同时使用
cin
和getline
时刚刚接触容易出现错误,使用cin
后通过回车读入字符,在getline
中第4行相当于无效了,通过添加getchar()
出去回车,这个地方不细心的话就常常出现错误,尤其实在作题的时候,可以把getchar()
去掉看看输出是什么结果string s1, s2, s3; cin >> s1 >> s2; getchar(); getline(cin,s3); cout << s1 << s2 << s3;
string的输出
string的输出主要两种方式
-
使用
cout
string s = "hello"; cout << s;
-
使用
printf
/*使用printf ? C中并没有string这个类型啊 那么他输出 的类型是什么呢? 猜测按照之前的方式就应该是%吧*/ string s = "hello"; printf("%s\n", s); //按照这样的输出方式,其实是错的,不知道的这个细节的话可以亲自试试 //正确做法是 string s = "hello"; printf("%s\n", s.c_str()); /*可能会有疑问为甚么 要用这么繁琐的方式呢?本来不就有 简单的方法吗?原因和使用scanf一样 主要还是因为遇到特 殊格式时使用,比如多种同时输出多种格式的数据时添加空格 使用cout在一行中写的太长~ ,其次是速度快 */
C++中的引用与传值
在C++中添加了一个引用型的数据类型符号表示为
&
, 你能你觉得这为什么和取地址的符号一样?其实引用是在变量前加上&
符号,传出参数就像这样int &a
,不知道它怎么使用也没关系我们从C中的swap的那个示例说起
-
使用C交换两个变量的值(有误)
#include <cstdio> void swap1(int a, int b){ int t = a; a = b; b = t; } int main(){ int a = 6, b = 10; swap1(a, b); printf("%d %d", a , b); //毫无疑问 ,输出为 6 , 10 我就不多解释啦~ return 0; }
-
使用C交换两个变量的值
其实真正想要使用函数的方式交换了两个数的值,是要使用指针的,而使用指针的这种凡是刚刚接触还是不好理解的~
#include <cstdio> //这里理解为声明 void swap1(int *a, int *b){ //这里的*a可不是函数参数中的那个*a哦~,此处*a意思时解引用 int t = *a; *a = *b; *b = t; } int main(){ int a = 6, b = 10; swap1(&a, &b); printf("%d %d", a , b); //输出为10 6 return 0; }
-
使用C++的引用方式
刚刚我们使用指针的方式完成了交换,但是多少还是有些麻烦的,接下来看看使用引用的方式怎么实现值的交换
#include <iostream> #include <cstdio> using namespace std; void swap1(int &a, int &b){ int t = a; a = b; b = t; } int main(){ int a = 6, b = 10; swap1(a, b); printf("%d %d", a , b);// 10 6 return 0; }
通过C++中使用引用的方法,在参数名前添加
&
,表示这个参数按照传引用(call by reference)的方式, 而不是C中(call by value),其实类似于指针的功能,意思就是按照值传递他就是单向的的传递,指针双向传递,而引用代替了C中的指针,通过传引用的凡是改变了实参的值
C++中的结构体
C++中省略了结构的声明的写法,当结构题定义完,直接写名字,不用去写
struct
啦~ ,如果在C中想不写struct
,你还用要用到typedef
#include <iostream>
#include <cstdio>
using namespace std;
struct stu{
string name;
int score;
};
int main(){
stu sarr[5]; //C++中的声明方式
struct sarrt[5]; //C中的声明方式
return 0;
}
STL初步学习
本篇首先引出基本vector,set,map,stack,queue这些作题中常常用到的集合及一些简单的使用,后续会在第二篇中总结全面点的使用方式,以及C++ 11中的特性。
vector的使用
-
vector的理解
vector意思就是矢量,其实vector就是动态数组,它的长度时不确定的,可以不用特意声明vector的长度,相比与 C中的数组我们必须要对它声明长度使用vector的时候需要引用头文件
#include <vector>
,using namepace std
-
vector主要的成员函数
函数名 功能 复杂度 size() 返回向量的个数 O ( 1 ) {O(1)} O(1) push_back(n) 在向量的末尾添加n O ( 1 ) {O(1)} O(1) begin() 返回指向向量开头的迭代器 O ( 1 ) {O(1)} O(1) end() 返回指向向量末尾的的后一个迭代器 O ( 1 ) {O(1)} O(1) insert(p, x) 在向量P的位置插入x O ( n ) {O(n)} O(n) erase§ 删除向量中位置p的元素 O ( n ) {O(n)} O(n) clear() 删除向量中所有的元素 O ( n ) {O(n)} O(n) pop_back() 删除向量的最后一个元素 O ( 1 ) {O(1)} O(1) resize() 给向量分配大小 O ( 1 ) {O(1)} O(1) -
具体使用方法
-
声明
#include <iostream> #include <cstdio> #include <vector> using namespace std; int main(){ //定义一个不定长度vector v1 vector<int> v1; //定义v1并指定v2大小为5 ,没有被赋值的元素默认为0 vector<int> v2(5); //定义v3的长度为3,默认值全部为 4 vector<int> v3(3, 4); vector<int> v4; //将不定长的vector设置长度为6,默认初始化的值为0 v4.resize(6); //这个要和v2区分一下 意思就是每个v5[i]都是一个vector //理解成二维数组 vector<int> v5[2]; return 0; }
-
基本函数使用
#include <iostream> #include <cstdio> #include <vector> using namespace std; void dispaly(vector<int> v){ //遍历vector for(int i = 0; i < v.size(); i++){ cout << v[i] << " "; } cout << endl; } int main(){ //声明一个vector v1 没有分配大小 vector<int> v1; v1.push_back(1); v1.push_back(2); dispaly(v1); // 1 2 //使用数组的形式 v1[1] = 3; dispaly(v1); // 1 3 v1.insert(v1.begin() + 1, 9); //尾部插入2 个 1 //v1.insert(v1.end(), 2, 1); dispaly(v1);// 1 9 3 v1.erase(v1.begin() + 1); dispaly(v1); //1 3 cout << v1.size() << endl; // 2 v1.resize(6);//没有被赋值的元素默认为0 cout << v1.size() << endl; // 6 dispaly(v1);//1 3 0 0 0 0 return 0; }
-
遍历
#include <iostream> #include <cstdio> #include <vector> using namespace std; int main(){ vector< int> v; v.push_back(1); v.push_back(2); v.push_back(3); v.push_back(4); //1. 通过数组的形式遍历 for(int i = 0; i < v.size(); i++){ cout << v[i]; } //2.通过迭代器的方式遍历后两种使用的少 for(vector<int>::iterator it = v.begin(); it != v.end(); it++){ cout << *it; } //3.简化迭代器的写法C++11中的特性 编译器根据初始值的类型直接判断变量的类型 for(auto it = v.begin(); it != v.end(); it++){ cout << *it; } //4. 使用ForEach for(int vt : v){ cout << vt; } //使用引用的方式可以修改它的值 for(int &vt : v){ vt *= 2; } //5.同样的使用ForEach,C++11中的新特性 for(auto it : v){ cout << it; } return 0; }
注意:如果之前没有了解C++的小伙伴看到第二种遍历的方法时,可能不知道
vector<int>::iterator
那个是什么东西,什么意思,iterator
的意思就是迭代器,它就类似于指针,理解为指针就OK啦~不太清楚的就看看接下来时怎么写的 ,还有不小心遍历扯的有点多了刚刚接触的话以看遍历就这么多,那不是心态炸了其实用的最多就是第一个遍历方法,第二个在vector中不用就是为了后面的内容铺垫,后面方法也不用重点看知道有就行,第二篇我们重点谈这个内容,目前先理解 这个iterator
, 用C写出遍历的意思如下#include <cstdio> //1. 用指针求和 int getsum1(int* begin, int* end){ int len = end - begin, sum = 0; for(int i = 0; i < len; i++){ sum += begin[i]; } return sum; } //2. 用指针求和 int getsum2(int* begin, int* end){ int sum = 0; for(int *it = begin; it != end; it++){ sum += *it; } return sum; } // 这个就和那个iterator十分的类似,理解为指针就是这个意思 void disply(int* begin, int* end){ for(int *it = begin; it != end; it++){ printf("%d ", *it); } } int main(){ int a[5] = {1, 2, 3, 4, 5}; printf("%d\n", getsum1(a, a + 5)); printf("%d\n", getsum2(a, a + 5)); disply(a, a + 5); return 0; }
-
set的使用
-
set的理解
set 是根据元素值进行排序的集合,所插入的元素在集合中唯一,不存在重复元素
-
set的常用函数
函数名 功能 复杂度 size() 返回set中元素的个数 O ( 1 ) {O(1)} O(1) clear() 清空set O ( n ) {O(n)} O(n) end() 返回指向末尾的迭代器 O ( 1 ) {O(1)} O(1) begin() 返回指向开头的迭代器 O ( 1 ) {O(1)} O(1) erase(key) 删除key O ( l o g n ) {O(log_{n})} O(logn) find(key) 搜索key返回指向key的迭代器 O ( l o g n ) {O(log_{n})} O(logn) insert(key) 向set中插入key O ( l o g n ) {O(log_{n})} O(logn) -
使用方法
#include <iostream> #include <cstdio> #include <set> using namespace std; int main(){ set<int> s; //插入 s.insert(1); s.insert(2); s.insert(3); //查找元素4 if(s.find(4) == s.end()){ cout << "Not Exist!"; } //查找 1 这个元素并输出, (s.find(1) != s.end())表示可以找到1这个元素 cout << (s.find(1) != s.end()) << end; //删除2 s.erase(2); //set集合的遍历,类比vector,看成指针,不理解可以看看我之前用C的那个遍历 for(set<int>::iterator it = s.begin(); it != s.end(); it++){ cout << *it; } //C++11新特性 for(auto it = s.begin(); it != s.end(); it++){ cout << *it; } //清空s中的元素 s.clear(); return 0; }
map的使用
-
map理解
map就是从键(key)到值(value)的映射, map 集合以键与值的组合为元素,每个元素拥有 1 个键和 1 个值,集合以值作为排序标 准。集合中各元素的键唯一,不存在重复。map很好用,他就像增强版的数组一样,例如我这么定义
map<string,int> m
(string 是key, int是value),赋值的时候就可以这样m["student"] = score
,其他类型同理~ -
map的常用函数
函数名 功能 复杂度 size() 返回map中元素的个数 O ( 1 ) {O(1)} O(1) clear() 清空map O ( n ) {O(n)} O(n) end() 返回指向末尾的迭代器 O ( 1 ) {O(1)} O(1) begin() 返回指向开头的迭代器 O ( 1 ) {O(1)} O(1) erase(key) 删除key O ( l o g n ) {O(log_{n})} O(logn) find(key) 搜索key返回指向key的迭代器 O ( l o g n ) {O(log_{n})} O(logn) insert(key,value) 向map中插入(key,value) O ( l o g n ) {O(log_{n})} O(logn) -
map的基本使用
#include <iostream> #include <cstdio> #include <map> using namespace std; int main(){ //定义 map<string,int> m; //添加元素 m["steves"] = 85; m["alan"] = 95; //插入元素 m.insert(pair<string, int>("emma", 90)); //删除 m.erase("emma"); //查找 pair<string, int> t = *m.find("alan"); cout<< t.first << " " << t.second << endl; //遍历 for(map<string, int>::iterator it = m.begin(); it != m.end(); it++){ cout << it->first << " " << it->second << endl; } for(auto it = m.begin(); it != m.end(); it++){ cout << it->first << " " << it->second << endl; } for(auto mt : m){ cout << mt.first << " " << mt.second << endl; } return 0; }
stack的使用
-
stack理解
stack 简单来说就是一个先进后出的数据结构 ,c实现栈的话就要手写,C++中引入
#include <stack>
即可 -
stack中常用的方法
函数名 功能 复杂度 size() 返回栈的大小 O ( 1 ) {O(1)} O(1) top() 返回栈顶元素 O ( 1 ) {O(1)} O(1) pop() 栈顶元素弹栈 O ( 1 ) {O(1)} O(1) push(x) x入栈 O ( 1 ) {O(1)} O(1) empty() 判空 O ( 1 ) {O(1)} O(1) -
使用方法
stack的使用的还是非常广泛的,比如经典的图的深度遍历,我用一个简单10 转 2概括一下stack使用
#include <iostream> #include <cstdio> #include <stack> using namespace std; int main(){ //声明 stack<int> s; //压栈 s.push(1); //栈的长度 cout << s.size() << endl; //弹栈 s.pop(); int n = 105; while(n > 0){ s.push(n % 2); n /= 2; } // 判空 while(!s.empty()){ //输出栈顶元素 cout << s.top(); s.pop(); } return 0; }
queue的使用
-
队列的理解
队列就是一种先进先出的数据结构,C中也需要手写,C++中引入
#include <queue>
即可 -
队列的常用方法
函数名 功能 复杂度 size() 返回对列的大小 O ( 1 ) {O(1)} O(1) front() 返回队头元素 O ( 1 ) {O(1)} O(1) pop() 取出队头元素 O ( 1 ) {O(1)} O(1) push(x) x入队 O ( 1 ) {O(1)} O(1) empty() 判空 O ( 1 ) {O(1)} O(1) back() 对位元素 O ( 1 ) {O(1)} O(1) -
queue中的使用
同样的queue的使用也是非常广泛的,比如图的广度遍历有兴趣可以看看,我在这里就简单演示一下啦
#include <iostream> #include <cstdio> #include <queue> using namespace std; int main(){ queue<int> q; for(int i = 0; i < 10; i++){ //入队 q.push(i); } cout << q.size() << endl; for(int i = 0; i < 10; i++){ //队头 队尾 cout << q.front() << " " <<q.back() << endl; q.pop(); } if(q.empty()) cout << "Not exist any elements\n"; return 0; }
总结
本篇主要写了由C到C++的过渡之间存在的差异,以及刷题中常常用到STL集合的基本使用,不是很全面。第二篇将对vector、set、 map的使用做个补充,以及其他类型做个全面补充例如 list unorderedmap,STL其他实用的库 例如algorithm等,还有C++11中好用的特性做一个全面的补充。