第五章 C++与STL入门
学习目的:
C++博大精深,相应的也伴随着许多让人诟病的地方,对于参加算法竞赛来说,掌握一些C++的特性将事半功倍的效果。
1. 从C到CC++
-
C++头文件的变更,即 在C的头文件前面加上一个小写字母c,然后去掉后缀
.h
,例如C语言头文件stdio.h
在C++里面就变成了cstdio
. -
C++特有的头部文件,标准输入输出流
iostream
,存在于名称空间std
中。- 当然C++流不是完美的,最大的缺点就是运行太慢,当输入量大时,请不要使用C++流输入
- 其次,解释下
using namespace std
的意思。C++有“名称空间(namespace)”的概念,用于缓解复杂程序的组织问题。将函数写到各自的名称空间里,然后调用。就相当于给其划定了“驻扎区”,要调兵时,直接对某驻扎下达命令就行了。如果代码和该名称空间的内容不重名,就可以用using namespace std
的方法,将std
里的名字导入到默认空间。
//推荐使用写法,使用using namespace std语句的方式 #include <iostream> using namespace std; int main(){ int n; cin >> n;//从输入流中读取一个int类型的数存放于n cout << n << endl;//输出n的值,并换行 return 0; } //不用using namespace std语句的方式书写 #include <iostream> int main(){ std : :cin >> n; std : :cout << n << endl; return 0; }
- 强调一点头文件
iostream
和提供常用算的头文件algorithm
里面定义的内容都是放在std
名称空间里的
//min函数就是在头文件algorithm中的 #include <iostream> #include <algorithm> int main(){ int a,b; cin >> a << b; cout << min(a,b) << endl; return 0; }
-
声明数组时,数组大小可以使用
const
声明的常数(C99中不允许),在C++中,更推荐这种写法 -
C++与C最显著的区别就是多了个bool来表示布尔值,然后用true和false来分别表示真假,相对于用
int
值来表示真假来说,这样写更使得程序清晰。这里在注意一点Java中int
值不能用来判断真假 -
"引用":C++里面如果,在参数名前面加一个"&"符,就表示这个参数按照“传地址”的方式传递,而不是C语言中的传值传入。所以,在函数内改变的值也会修改函数的实参。C++中的引用就是变量的“别名”,它可以在一定程度上代替C的指针
-
C++在
sting
头文件里定义了string
类型,直接支持流式读写,string
有很多方便的函数和运算符,但速度较慢。另外string
类型还可以像整数那样“相加”。 -
stringstream
在头文件sstream
中,虽然它们很方便,但是sstream
比stream
更慢,应该谨慎使用。 -
C++里面除了修改了C结构体的部分内容,同时还支持
class
类。一般用struct
定义“纯数据类型,而class
定义的是”拥有复杂行为“的类型。简单来说,复杂的内容用class
就对了。 -
C++中的结构体除了可以拥有成员变量(用
a.x
的方式来访问)之外,还可以拥有成员函数(用a.add(12
的方式访问))。C++里sturct
的很多概念和写法同样适用于class
。 -
C++中结构体可以有一个或多个构造函数,在声明2变量是调用。
-
C++中的函数(不只是构造函数)参数拥有默认值。
-
在C++结构体的成员函数中
this
是指向当前对象的指针,这一点结合java中this
的用法及含义就明白了。
#include<iostream>
using namespace std;
struct Point{
int x,y;
//这样的函数称为构造函数,"=0"就是设置了0为默认值,如果这两个参数没有被指明,则就按照0来处理。
Point(int x = 0 , int y = 0) : x(x),y(y)){}//这里的x(x),相当于this.x = x
};
//"+"运算符的重载,重新定义了 "+"运算符。
Ponitn operator + (const Point& A,const Point& B){
return Point (A.x+B.x,A.y+B.y);
}
- 模板:函数、结构体和类是可以带模板的。修改原有的函数,增加其使用范围,用法如下
//套用上面的结构体,就能使得其适用的范围更广
template <typename T>
struct Point {
T x,y;
Point (T x = 0 , T y = 0):x(x),y(y){}
}
//更改以后,就可以同时使用int型和double型的Point了
STL初步
STL是指C++标准模板库(Standard Template Library)。
排序与检索
-
algorithm
头文件中sort
可以给任意对象排序,包括内置类型和自定义类型,前提是定义了"<"运算符。排序后可以用lower_bound
查找大于或者等于x的第一个位置。待排序/查找的元素可以放在数组里,也可以放在vector
里。 -
sort
的使用:sort(a,a+n)
,表示对a
数组的前n个元素进行排序。当然,在vector
中调用方式就为sort(v.begin() , v.end())
。
不定长数组 : vector
1. `vector`是个不定长数组,具有”封装“的特性,如果`a`是个`vector`,那么可以用`a.size()`来读取它的大小,`a.resize()`来改变大小,`a.push_back()`像尾部添加元素,`a.pop_back()`来删除最后一个元素。
2. `vector`是一个模板类,所以需要用`vector<int>a`或者`vector<double>b`这样的方式来声明一个`vector`。`vector<int>`是一个类似于`int a[]`的整数数组,而`vector<string>`就是一个类似于`string[]`的字符串数组。
3. 取出指令之间的共同点,编写函数减少代码,是一种值得推荐到方法。
4. `vector`头文件中的`vector`是一个不定长数组,可以用`clear()`清空,`push_back()`和`pop_back()`在尾部添加和删除元素,用`empty()`测试是否为空,`vector`之间可以直接赋值或者作为函数的返回值。
集合:set
-
set
就是数学上的集合,——每个元素最多出现一次。就和sout
一样,自定义的类型就可以构造set
,当必须定义”小于”运算符。 -
iterator
的意思是迭代器,是STL中的重要概念,类似于指针。 -
//set的用法 #include <iostream> #include <set> using namespace std; int mian(){ set<int>s; //定义一个空集合s for(int i = 0; i < 8; i++){ s.insert(i); //将i的值插入到集合s中 } for(auto it = s.begin();it != s.end(); it++){ cout << *it << endl; //输出s集合中所有的元素。注意,制力有一个 "*"。 } if(s.find(5) != s.end() ) { //表示能找到5,因为s.end()表示的是最后一位元素的后一位。 cout << "Found 5" << endl; }else { cout << "Not Found" << endl; } }
映射:map
-
map就是键到值的映射,就类似于身份标记。
map
会自动将所有的键值对按照键从小到大的排序。 -
map重载了
[]
运算符,map像是数组的“高级版” -
set
头文件中的set
和map
分别就是集合与映射。二者都支持insert
、find
、count
和remove
操做。并且可以按照从大到小的顺序循环遍历其中的元素。map
还提供了"[]"运算符,使得map
可以像数组一样使用。事实上,map
也成为“关联数组”//mapp的用法 #include <iostream> #include <map> #include <string> using namespace std; int main(){ map<string , int>m //定义一个新的键值对,键是string类型的,值是int类型。 m["Miubai"] = 1234567;//将键为 "Miubai",值为 "1234567"的值放入map中 cout << m["Miubai"] << endl;//输出map中Miubai所对应的值 //遍历,输出map所有元素,键使用it->first获取,值用it->second获取 for (auto it = m.begin(); it != m.end();i++){ cout << it->first << " " << it->second << endl; } //输出map的第一个元素,输出它的键和值 cout << m.begin()->first << " 值是 " << m.begin()->second << endl; //输出map元素的最后一个元素,及其键和值 cout << m.rbegin->first << "值是" << m.rebin->second << endl; //输出map元素的个数 cout << m.size() << endl; return 0; }
栈、队列与优先队列
-
栈:就是符合“后进先出”的规则的数据结构。有压栈(PUSH)和弹栈(POP)两种操作。弹栈是将栈顶元素弹出。
-
STL的栈定义在头文件
stack
中,用stack<int>s
的方式定义,用push()
和pop()
实现元素的入栈和出栈操做。top()
取出栈顶元素(但不删除)。//栈的用法 #include <iostream> #include <stack> using namespace std; int main() { stack<int>s;//定义一个空栈S for(int i = 0;i < 9;i++){ s.push(i*20);//将i*20的值压栈进入栈s中 } cout << s.top() << endl; //访问s的栈顶元素 cout << s.size() << endl;//输出s的元素个数 s.pop()//移除栈顶元素 return 0; }
-
STL的
queue
头文件提供了队列,用queue<int>s
方式定义,用push()
和pop()
进行元素的入队和出队操做,front()
取出首元素(同样也不会删除)。 -
STL的优先队列也定义在文件
<queue>
里,用priority_queue<int>pq
来声明。其中pq
是一个“越小的整数优先级越低的优先队列”。由于出对元素并不是最先进队的元素,出队的方法由queue
的front()
变成了top()
。//queue的使用 #include <iostream> #include <queue> using namespace std; int main() { queue<int>Q //定义一个空队列Q int n; for(int i = 0; i < 10;i++){ cin >> n; Q.push(n);//将n的值压入队列Q中 } cout << Q.front() << "队尾元素:" << Q.back() << endl; //访问队首元素和队尾元素 cout << Q.size() << endl; //输出队列元素的个数 q.pop();//移除队列队首元素 return 0; }
测试STL
库不一定没有bug,使用之前要养成测试库的好习惯
- 随机数发生器,其核心函数是
ctdlib
中的rand()
。它能生成一个闭区间[0,RAND_MAX] (RAMND_MAX至少为32767)内的均匀随机整数,即该区间每个值被随机获取的概率一样。测试STL,此函数能提供不少的帮助。 - 不要在同一个程序每次生成随机数之前都重新调用一次
srand
,务必只在程序开头调用一次srand
,而不要在同一个程序中多次调用。 - 可以用
cstdlib
中的srand
函数初始化随机种子。如果需要程序每次执行时使用一个不同的种子,可以使用ctime
中的time(NULL)
为参数调用的srand
,这样,在不同时间生成的随机数都是不一样的。一般来说,只在程序开头调用一次srand
。
文中部分参考过柳婼 の 的博客