文章目录
第1讲 C++入门及简单的顺序结构
变量的类型转换
int y = (int)x;
第2讲 printf语句与判断结构
printf输出格式
#include <cstdio>
见C
第3讲 循环结构
do while不管条件的值如何,都要至少执行一次循环
第4讲 数组
练习题1: 使用数组实现求斐波那契数列的第 N 项。
int n;
int f[100];
cin >> n;
f[0] = 0, f[1] = 1;
for (int i = 2; i <= n; i ++ ) f[i] = f[i - 1] + f[i - 2];
cout << f[n] << endl;
第5讲 字符串
常用ASCII值:‘A’- 'Z’是65 ~ 90,‘a’ - 'z’是97 - 122,0 - 9是 48 - 57
gets函数在新版C++中被移除了,因为不安全。
可以用fgets代替,但注意fgets不会删除行末的回车字符
char str[100];
fgets(str, 100, stdin);
C库
#include <string.h>
可变长的字符序列,比字符数组更加好用
#include <string>
- 初始化
string s4(10, 'c'); // s4的内容是 "cccccccccc"
- 不能用printf直接输出string,需要写成:
printf(“%s”, s.c_str())
- 使用getline读取一整行
getline(cin, s);
- 注意size()是无符号整数,因此
s.size() <= -1
一定成立 - 当把string对象和字符字面值及字符串字面值混在一条语句中使用时,必须确保每个加法运算符的两侧的运算对象至少有一个是string
string s5 = "hello" + ", "; // 错误:两个运算对象都不是string
string s7 = "hello" + ", " + s2; // 错误:不能把字面值直接相加,运算是从左到右进行的
第6讲 函数
形参也可以设置默认值,但所有默认值必须是最后几个。当传入的实参个数少于形参个数时,最后没有被传入值的形参会使用默认值。
当函数的形参为引用类型时,对形参的修改会影响实参的值。使用引用的作用:避免拷贝、让函数返回额外信息。
没有返回值的return语句只能用在返回类型是void的函数中。返回void的函数不要求非得有return语句,因为在这类函数的最后一句后面会隐式地执行return。
第7讲 类、结构体、指针、引用
结构体和类的作用是一样的。不同点在于类默认是private,结构体默认是public。
第8讲 STL
#include <vector>
vector是变长数组,支持随机访问,不支持在任意位置 O(1) 插入。为了保证效率,元素的增删一般应该在末尾进行。
- 声明
vector<int> a; // 相当于一个长度动态变化的int数组
vector<int> b[233]; // 相当于第一维长233,第二位长度动态变化的int数组
- clear函数把vector清空
- 迭代器就像STL容器的“指针”,可以用星号*操作符解除引用。
一个保存int的vector的迭代器声明方法为:
vector<int>::iterator it;
- begin/end
begin函数返回指向vector中第一个元素的迭代器。例如a是一个非空的vector,则*a.begin()
与a[0]
的作用相同。
所有的容器都可以视作一个“前闭后开”的结构,end函数返回vector的尾部,即第n 个元素再往后的“边界”。*a.end()
与a[n]
都是越界访问,其中n = a.size()
for (vector<int>::iterator it = a.begin(); it != a.end(); it ++) cout << *it << endl;
- front/back
front函数返回vector的第一个元素,等价于*a.begin()
和a[0]
。
back函数返回vector的最后一个元素,等价于*--a.end()
和a[a.size() – 1]
。 - push_back()和pop_back()
a.push_back(x)
把元素x插入到vector a的尾部。
b.pop_back()
删除vector a的最后一个元素。
#include <queue>
主要包括循环队列queue和优先队列priority_queue两个容器
- 声明
queue<int> q; struct rec{…}; queue<rec> q; //结构体rec中必须定义小于号 priority_queue<int> q; // 大根堆 priority_queue<int, vector<int>, greater<int>> q; // 小根堆 priority_queue<pair<int, int>>q;
- 循环队列queue
push // 从队尾插入 pop // 从队头弹出 front // 返回队头元素 back // 返回队尾元素
#include <stack>
push // 向栈顶插入
pop // 弹出栈顶元素
#include <deque>
双端队列deque是一个支持在两端高效插入或删除元素的连续线性存储空间。它就像是vector和queue的结合。与vector相比,deque在头部增删元素仅需要 O(1) 的时间;与queue相比,deque像数组一样支持随机访问。
[] // 随机访问
begin/end // 返回deque的头/尾迭代器
front/back // 队头/队尾元素
push_back // 从队尾入队
push_front // 从队头入队
pop_back // 从队尾出队
pop_front // 从队头出队
clear // 清空队列
#include <set>
主要包括set和multiset两个容器,分别是“有序集合”和“有序多重集合”,即前者的元素不能重复,而后者可以包含若干个相等的元素。set和multiset的内部实现是一棵红黑树,它们支持的函数基本相同。
- 声明
set<int> s; struct rec{…}; set<rec> s; // 结构体rec中必须定义小于号 multiset<double> s;
- size/empty/clear
- 迭代器
set和multiset的迭代器称为“双向访问迭代器”,不支持“随机访问”,支持星号*解除引用,仅支持++和–两个与算术相关的操作。
set<int>::iterator it;
- begin/end
s.begin()
是指向集合中最小元素的迭代器。
s.end()
是指向集合中最大元素的下一个位置的迭代器。换言之,就像vector一样,是一个“前闭后开”的形式。因此-- s.end()
是指向集合中最大元素的迭代器。 - insert
s.insert(x)
把一个元素x插入到集合s中,时间复杂度为 O(logn)。
在set中,若元素已存在,则不会重复插入该元素,对集合的状态无影响。 - find
s.find(x)
在集合s中查找等于x的元素,并返回指向该元素的迭代器。若不存在,则返回s.end()。时间复杂度为 O(logn)。 - lower_bound/upper_bound
与find类似,但查找的条件略有不同,时间复杂度为 O(logn)。
s.lower_bound(x)
查找大于等于x的元素中最小的一个,并返回指向该元素的迭代器。
s.upper_bound(x)
查找大于x的元素中最小的一个,并返回指向该元素的迭代器。 - erase
设it是一个迭代器,s.erase(it)
从s中删除迭代器it指向的元素,时间复杂度为 O(logn)。
设x是一个元素,s.erase(x)
从s中删除所有等于x的元素,时间复杂度为 O(k+logn),其中 k 是被删除的元素个数。
#include <map>
map容器是一个键值对key-value的映射,其内部实现是一棵以key为关键码的红黑树。Map的key和value可以是任意类型,其中key必须定义小于号运算符。
- 声明
map<key_type, value_type> name;
- size/empty/clear/begin/end
- insert/erase
参数均是pair<key_type, value_type>
- find
h.find(x)
在变量名为h的map中查找key为x的二元组。 - []操作符
h[key]
返回key映射的value的引用,时间复杂度为 O(logn)
通过h[key]来得到key对应的value,还可以对h[key]进行赋值操作,改变key对应的value。
第9讲 位运算与常用库函数
- 位运算
符号 运算
& 与
| 或
~ 非
^ 异或
>> 右移
<< 左移
- 常用操作:
求x的第k位数字x >> k & 1
lowbit(x) = x & -x
,返回x的最后一位1 - 常用库函数
- reverse翻转
reverse(a.begin(), a.end());
翻转一个数组,元素存放在下标1 ~ n:
reverse(a + 1, a + n + 1);
- unique去重
返回去重(只去掉相邻的相同元素)之后的尾迭代器(或指针),仍然为前闭后开,即这个迭代器是去重之后末尾元素的下一个位置。
该函数常用于离散化,利用迭代器(或指针)的减法,可计算出去重后的元素个数。
把一个vector去重:
int m = unique(a.begin(), a.end()) – a.begin();
把一个数组去重,元素存放在下标1 ~ n:
int m = unique(a + 1, a + n + 1) – (a + 1);
- random_shuffle随机打乱
用法与reverse相同 - sort
对两个迭代器(或指针)指定的部分进行快速排序。可以在第三个参数传入定义大小比较的函数,或者重载“小于号”运算符。- 传入比较函数
int a[MAX_SIZE]; bool cmp(int a, int b){ return a > b;} sort(a + 1, a + n + 1, cmp);
- 自定义结构体,重载“小于号”运算符
struct Rec{ int id, x, y;}; vector<Rec> a; bool operator <(const Rec &a, const Rec &b){ return a.x < b.x || a.x == b.x && a.y < b.y;} sort(a.begin(), a.end());
- 传入比较函数
- lower_bound/upper_bound 二分
lower_bound的第三个参数传入一个元素x,在两个迭代器(指针)指定的部分上执行二分查找,返回指向第一个大于等于x的元素的位置的迭代器(指针)。
upper_bound的用法和lower_bound大致相同,唯一的区别是查找第一个大于x的元素。当然,两个迭代器(指针)指定的部分应该是提前排好序的。
在有序int数组(元素存放在下标1 ~ n)中查找大于等于x的最小整数的下标:
int i = lower_bound(a + 1, a + 1 + n, x) - a;
在有序vector中查找小于等于x的最大整数(假设一定存在):
upper_bound的用法和lower_bound大致相同,唯一的区别是查找第一个大于x的元素。当然,两个迭代器(指针)指定的部分应该是提前排好序的。
在有序int数组(元素存放在下标1 ~ n)中查找大于等于x的最小整数的下标:
int i = lower_bound(a + 1, a + 1 + n, x) - a;
在有序vector中查找小于等于x的最大整数(假设一定存在):
int y = *--upper_bound(a.begin(), a.end(), x);
- reverse翻转