c++初期笔记(超详细超完整)
语法基础
一. 变量,输入输出,表达式,和顺序语句
|
using namespace std;用于创建命名空间,cout和cin都属于其中
(1) 布尔型 存储true和flase 1byte
(2) 字符型 char 1byte
(3) 整型 int 4byte
(4) 浮点型 实数和科学计数法
float 有效数字6-7位 4byte
double 有效数字15-16位 8byte
long double 18-19位
一个字节有8比特 1byte=8bit 1KB = 1024byte
cin>>a>>b;表示输入a和b
cout<<a+b<<endl;表示输出a+b;
AC accepted
WA wrong answer
time limit error 超时
memory limit error 内存超限
segmentation fault 数组越界
compiler error 编译错误
presentation error 表达错误,格式错误
runtime error string字符串检测到越界
Float Point Exception某处位置存在a%i时,i为0的情况
输出结果为nan(not a number)的原因,所要开根号的数可能是个负数
输入输出同样可以用scanf和printf
万能头文件 <bits/stdc++.h> 包含所有常用的函数
运行速度:<bits/stdc++.h 大于
区别在于头文件大小
iostream可以读入各种常见类型的变量,也可以读入string字符串
printf的三种排版格式
1.%5d 每次输出靠右占用五个字符
2.%-5d 每次输出靠左占用五个字符
3.%05d 每次输出靠右占用五个字符,不足五个以0填充
二. 判断语句
if语句
1.if语句之后千万不要加分号 (如果加了分号表示if执行了一个空语句)
2.其余用法与c语言相同
using namespace std; |
注意点:判断语句里判断符号分别为”>”,”<”,“>=”, “<=”,“!=”, “==”
3.逻辑表达式
名称 写法 另一种写法
(1) 逻辑与:&&,and
(2) 逻辑或:||, or
(3) 逻辑非:! not
如果直接写if (a >b>c),其真正意义是,先判断a是否大于b,如果a大于b,则a>b将作为布尔值1与c比较,如果a小于b,则作为布尔值0与c比较
!a等价于a != -1
printf等函数使用时会有“%”等特殊符号的使用,在printf函数里,%是指的输出格式,即转义字符,如果仅为了打印“%”,需要将%前加“%”,
例如printf(“sum = %d%%“,sum);
三. 循环结构(基本和c语言相同)
1.while循环:while循环表达式可以嵌入cin等函数, while(cin>>n),因为cin函数是存在返回值的,如果没有返回值是因为读到文件结束符
小技巧:while 循环输入x时可以写成while (cin>>x&&x)表示输入x且读入x时循环结束
c++中的EOF有准确的值,即-1.
cin和scanf的区别:(1).在输入字符时cin会自动过滤掉空格,而scanf会读入空格
(2).cin的运算效率比scanf慢很多,但如果输入的数是小于10000的情况下,程序员用cin编写代码的效率会更快一些
c++一秒只能运算1亿次,必要的时候需要优化代码
2.reverse函数
·作用:将数组中某一连续的元素旋转 比如1 2 3 4 5 逆序一次得到5 4 3 2 1
·函数库函数:
· 函数使用 reverse(a,a+n)
· 说明:将a数组中从首元素到第n个元素的子数组逆序排布
· 旋转:比如数组1 2 3 4 5旋转一次得到5 1 2 3 4,再旋转得到4 5 1 2 3
四. 数组
浮点数与整型的存储方式不同,且当运算量过大时都会存在一定误差
mem函数(库函数cstring)
1.memset
1.格式
memset(arr,1,sizeof arr)
2.意义:
将arr数组全部赋值为1 ,sizeof arr并不是代表几个数,而是说没前进一步占几个字节
利用memset对数组赋值会比循环快很多
2.memcpy
1.格式
memcpy(arr1,arr2,sizeof arr2)
意义
将arr1数组赋值为arr2数组中的对应元素
五. 字符串
1.字符数组:由若干个字符组成的数组,一种另类的字符串
2.字符串:由若干个字符组成
数组定义的字符串的长度一般比真实的长度多一,因为每个字符数组后面都会以‘\0‘这个结束符结尾
读入的字符串较小时适合用字符数组表示,读入的字符串较大时适合用string字符串表示,scanf无法读入string定义的字符串
|
a1没有’\0’这个结束符,本质上是一个字符数组
a2有了结束符,可以算作一个字符串
a3以字符串定义,就是字符串,但因为使用数组定义的,长度会多1
3.cin,cout函数本身也是通过地址来读入,打印结果
比如cout<<str<<endl;就是从str的首元素开始打印
而cout<<str+1<<endl;就是从str的第二个元素开始打印
输入字符数组时读到空格时输入会停止,比如输入abc def,真正输入进去只有abc
4.fgets(s,n,stdin) 可以读入空格,不会过滤回车
说明:读入n长度的字符串s,s字符串是
库函数:stdio
5.getline(cin,s) 可以读入空格
说明:读入一行字符串s,且字符串s必须用string定义,常用
库函数:iostream
6.c中string库函数
(1).strlen,求出字符串长度,不计‘\0‘
(2).strcmp(arr1,arr2)按字典序比较两个字符串的大小,返回值为1,0,-1
(3).strcpy(arr1,arr2)将arr2字符串复制给arr1
在for循环里用strlen,每次循环都会计算一次strlen,所以需要提前定义一个变量存储长度,减少运算量
7.标准库函数string(可变长的字符序列,比字符数组更好用)
·优点:
(1)
能直接赋值
|
(2)可直接输入输出
#1nclude <cstd1o> |
|
其中s1.c_str()返回了一个字符数组
(4) 字符串可直接比较,无需strcmp
(5) 字符串可以直接相加,即拼接,甚至累加,(string类型)“hello ”+(string类型)“world”-》“Hello World”
字符串相加时必须保证两者之间必须保证有一个字符串属于string类型
最好别用+=来增添字符串
遍历初识:
|
顺次遍历s字符串中的每一个元素
该遍历等价于
for(int i = 0;i < s.size(); i++) |
并同时输出每一次的c,但原先的字符串并不受到影响
8.substr函数
(1) 说明,返回特定长度的字符串
(2) 格式:str.substr(0,len),返回从str首元素地址开始一直到len的字串,len可以省略即str.substr(0),指的是返回从下标0开始一直到结束的字符串
(3) 库函数:string
9.stringstream ssin函数
(1)说明:将字符串作为字符串流通过空格分隔开,每一个分隔开的字符串作为原字符串的字串
(2)格式stringstream ssin(s)
|
str被不断赋值成字符串的一个字串
(3)库函数 sstream
10.ssin函数(通常叫ssin,也可以自定义昵称使用)
(1) 说明:从字符串流中提取我们需要的信息,可以是各种类型的变量,和cin等价
(2) 格式 ssin>>a>>b>>str
int main() |
(3) 库函数 sstream
11.sscanf函数
(1)说明:作用与ssin相同,但格式上多了一个参数
(2)格式 sscanf(s,“%d %c %s”,&a,&b,&str);
|
(3)库函数cstdio
(1) s.back(); 返回字符串s的最后一个元素
(2) s.pop.back();删除字符串s的最后一个元素
(3) 库函数 iostream
六. 函数(使编代码的过程更加简洁)
组成部分:返回类型,函数名,参数,函数体
函数可以先声明再定义,也可以只是定义,但只有函数定义时函数必须放在main函数之前
函数的形参列表可以为空,但是不能省略
把数组传入函数中,对数组某一元素赋值,原数组会发生相应改变,因为传入函数中的是数组的地址
1.静态变量 static(在函数内部开辟了一个只有该函数所能用的的全局变量)
·说明:每次函数调用所使用的都是同一个变量,只会被定义一次,下次调用定义静态变量时会直接跳过,变量的值会在改变前一直保存下去
·作用:可以检测函数调用的次数
·使用:static int cnt = 0;
int output(void) |
当局部变量和全局变量重名时,优先使用局部变量
2.参数传递
(1) 传值调用
·相当于拷贝了一份所传的变量,原变量的值不受影响
(2) 传址调用,传引用调用
·将变量传到函数内部,传进去的是变量本身,会因函数的过程而改变
int max(int &x,int &y) |
(3) 数组传参
在函数中对数组的修改,会影响外部的数组
与c同样的,数组的行也可以省略
3.inline修饰函数
·说明:相当于把函数的函数体直接在main函数中展开
·举例:
|
·作用:会让程序运行时快一些
4.函数递归
·说明:函数调用函数本身
·缺点:运行速度慢
七. 类,结构体,指针与引用
1.类(class)
(1). 说明:可以将变量,数组和函数完美的打包在一起
(2). 结构:两个主元素private,public
·private只能在类内部
·public可以在外部调用,也可以在内部调用
·private和public可以多次定义
·类中的变量和函数统一称为成员变量
using namespace std; |
注意点:花括号后面一定要加分号
class Person |
(3)类的调用
int main( ) |
c.age是错误的,因为age属于类中private的变量,无法在外部调用
2.结构体(与类的用法相同)
3.类与结构体的区别:如果不在内部定义private和public时定义的变量,类默认定义的成员变量属于private,结构体默认定义的成员变量属于public
4.类与结构体可以在内部构造函数
struct Person |
特殊的赋值方式
Person() {} |
第12行表示定义一个函数,将)_age值赋给age,将 _height赋给height
5.指针
(1) 指针所存的就是变量的地址
(2) c++中输出变量的地址
|
如果定义的变量是连续定义的,那么他们的地址也是连续的
(3) 指针的引用
int main() |
int* p = &a表示定义一个整型指针存储a的地址
之后的*p则表示解引用p,对p的地址读取一个值
并且可以修改这个值
数组是一种特殊的指针,数组名存的是数组首元素的地址
每个int型变量有四个字节,所以整型数组每一个元素的地址相差四个字节
(4) 指针也可以进行运算
如果运算的指针属于整型,会在运算完之后除以整型字节数4
6.引用
说明:c++指针写法的简化
举例:
int main( ) |
此时之后使用的p都是*p,p相当于是a的别名
7.链表
示例
|
其中定义结构体变量node并用指针获得地址可以写成一段
int main() |
此时如果要调用结构体内的成员变量,则用->指向该变量
Node* p = new Node(1); |
如果p不是指针,则要用 . 来调用
链表的第一个节点的地址通常称为头节点,我们习惯定义head保存第一个节点的地址
8.链表遍历
使pqo分别为1,2,3节点,定义head作为头节点,将p节点1赋值给head,通过for循环一次遍历每一个节点
·如何将节点删除
本质上是在遍历过程中跳过该节点
9.条件表达式的一种应用
·短路
在a&&b时,只要a条件不成立,则无需判断b是否成立,直接跳过b语句,实际上,这个条件表达式并非一定要放在if表达式中
八. STL
1.vector
一种可以自定义长度的数组
数组定义方式 vector
定义二维数组 vector
结构体的成员变量也可以动态开辟
接下来镶嵌yxc的讲义(实在太长了)
—————————————————————————————
第八章 C++ STL
STL是提高C++编写效率的一个利器。
——闫学灿
1.#include
vector是变长数组,支持随机访问,不支持在任意位置O(1)插入。为了保证效率,元素的增删一般应该在末尾进行。
声明
#include
vector
vector
struct rec{…};
vector
size/empty
size函数返回vector的实际长度(包含的元素个数),empty函数返回一个bool类型,表明vector是否为空。二者的时间复杂度都是O(1)。
所有的STL容器都支持这两个方法,含义也相同,之后我们就不再重复给出。
clear
clear函数把vector清空。
迭代器
迭代器就像STL容器的“指针”,可以用星号“*”操作符解除引用。
一个保存int的vector的迭代器声明方法为:
vector
vector的迭代器是“随机访问迭代器”,可以把vector的迭代器与一个整数相加减,其行为和指针的移动类似。可以把vector的两个迭代器相减,其结果也和指针相减类似,得到两个迭代器对应下标之间的距离。
begin/end
begin函数返回指向vector中第一个元素的迭代器。例如a是一个非空的vector,则*a.begin()与a[0]的作用相同。
所有的容器都可以视作一个“前闭后开”的结构,end函数返回vector的尾部,即第n个元素再往后的“边界”。*a.end()与a[n]都是越界访问,其中n=a.size()。
下面两份代码都遍历了vector
for (int I = 0; I < a.size(); I ++) cout << a[i] << endl;
for (vector
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的最后一个元素。
2.#include
头文件queue主要包括循环队列queue和优先队列priority_queue两个容器。
声明
queue
struct rec{…}; queue
priority_queue
priority_queue<int, vector
priority_queue<pair<int, int>>q;
循环队列 queue
push 从队尾插入
pop 从队头弹出
front 返回队头元素
back 返回队尾元素
优先队列 priority_queue
push 把元素插入堆
pop 删除堆顶元素
top 查询堆顶元素(最大值)
3.#include
头文件stack包含栈。声明和前面的容器类似。
push 向栈顶插入
pop 弹出栈顶元素
4.#include
双端队列deque是一个支持在两端高效插入或删除元素的连续线性存储空间。它就像是vector和queue的结合。与vector相比,deque在头部增删元素仅需要O(1)的时间;与queue相比,deque像数组一样支持随机访问。
[] 随机访问
begin/end,返回deque的头/尾迭代器
front/back 队头/队尾元素
push_back 从队尾入队
push_front 从队头入队
pop_back 从队尾出队
pop_front 从队头出队
clear 清空队列
5.#include
头文件set主要包括set和multiset两个容器,分别是“有序集合”和“有序多重集合”,即前者的元素不能重复,而后者可以包含若干个相等的元素。set和multiset的内部实现是一棵红黑树,它们支持的函数基本相同。
声明
set
struct rec{…}; set
multiset
size/empty/clear
与vector类似
迭代器
set和multiset的迭代器称为“双向访问迭代器”,不支持“随机访问”,支持星号(*)解除引用,仅支持”++”和–“两个与算术相关的操作。
设it是一个迭代器,例如set
若把it++,则it会指向“下一个”元素。这里的“下一个”元素是指在元素从小到大排序的结果中,排在it下一名的元素。同理,若把it–,则it将会指向排在“上一个”的元素。
begin/end
返回集合的首、尾迭代器,时间复杂度均为O(1)。
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是被删除的元素个数。
count
s.count(x) 返回集合s中等于x的元素个数,时间复杂度为 O(k +logn),其中k为元素x的个数。
- #include
map容器是一个键值对key-value的映射,其内部实现是一棵以key为关键码的红黑树。Map的key和value可以是任意类型,其中key必须定义小于号运算符。
声明
map<key_type, value_type> name;
例如:
map<long, long, bool> vis;
map<string, int> hash;
map<pair<int, int>, vector
size/empty/clear/begin/end均与set类似。
Insert/erase
与set类似,但其参数均是pair<key_type, value_type>。
find
h.find(x) 在变量名为h的map中查找key为x的二元组。
[]操作符
h[key] 返回key映射的value的引用,时间复杂度为O(logn)。
[]操作符是map最吸引人的地方。我们可以很方便地通过h[key]来得到key对应的value,还可以对h[key]进行赋值操作,改变key对应的value。
—————————————————————————————
卡特兰数递归公式(隶属二叉树): f(n) = f(n-1)* f(0)+ f(n-2)* * f(1) + …… + f(1) * f (n - 2) + f(0) * f(n-1);