2022/02/01 算法笔记学习
1. C语言基础
1.1 基本数据类型
1.2.1 变量
- int:,32位,4Byte,简要记忆:绝对值在10^9范围内的整数,%d
- 64位,8Byte,long long型,赋大于2^31-1的初值,后面需要加上LL, 10的18次幂方以内,输入输出==%lld==
- float型:精度6~7位,输出格式都为==%f==
- double型:精度15~16位 ,输入==%lf , 输出为%f==,浮点型尽量用double
- 小写字母比大写字母ASCII码值大32,直接赋值字符需要使用单引号,输入输出为==%c==
- 转义字符\n(换行)\0(空字符NULL,ASCII为0),输出直接在引号里面就行
- 字符串常量,赋值给字符数组。输入输出用==%s==,输入格式符变量地址前不用加取地址运算符&
- 布尔型,头文件stdbool.h
- 强制类型转换
- 符号常量
#define 标识符 常量 //末尾不加分号
#const 数据类型 变量名 = 常量;//更加推荐
#define 标识符 语句或片段
#define ADD(a,b) ((a)+(b)) //先替换再运行,需要用括号
//提前定义优先级
1.1.2 运算符
注意位运算符的一种用法
const int INF = (1<<30)-1;//必须加括号,位运算符优先级低于算术运算符
const int INF = 0x3fffffff;
1.2 输入输出
2022/02/02
1.2.1 scanf与printf输入输出
scanf("格式控制“,变量地址)
注意数组名本身就代表地址,不用加取地址运算符
-
scanf对除了%c以外的格式符的输入以== 空白符(空格,Tab,换行) ==为结束判断标志
-
当scanf遇见第一个非对应数据类型的字符,就得出结论,已经读到尾部,它会将这个字符放回输入;即意味着当程序下一次读取输入时,将从前面被放弃的那个字符开始
-
%c格式可以读入空格跟换行
#include<cstdio>
using namespace std;
int main(){
char a[10],b[10];
char c,d,e;
scanf("%s%s",a,b);
scanf("%c%c%c",&c,&d,&e);
printf("%s\n%s\n",a,b);
printf("%c%c%c",c,d,e);
}
输入:
123 234
1 2 3 4
输出:
123
234
1
结论:c,d,e分别是换行符、1、空格
- 若要输出”%“”",前面再加一个%或者\
- %md——不足m位的int型变量以m位进行右对齐输出,高位用空格补齐,若本身超过m位,保持原样
- %0md——用0补齐而不是空格
- %.mf——让浮点数保留m位小数输出,四舍六入五成双
a.被修约的数字小于5时,该数字舍去;
b.被修约的数字大于5时,则进位;
c.被修约的数字等于5时,要看5前面的数字,若是奇数则进位,若是偶数则将5舍掉,即修约后末尾数字都成为偶数;若5的后面还有不为“0”的任何数,则此时无论5的前面是奇数还是偶数,均应进位。
1.2.2 getchar()与putchar(someChar)
输入输出单个字符,包括空白符
C++也包含了该函数
#include<cstdio>
using namespace std;
int main(){
char c,d,e;
getchar();
c=getchar();
d=getchar();
putchar(c);
putchar(d);
}
1.2.3 C++的输入流与提取运算符>>
#include<iostream>
该头文件包含两种数据类型的定义——istream和ostream,分别表示输入流和输出流,以及以下声明
istream cin;
ostream cout;
提取运算符>>:有两个操作数,左操作数为一个流表达式,右操作数为一个可以向其存储输入数据的变量名(而在插入运算符中右侧项目包括:常量、变量或复杂表达式)(因为只有变量名才能引用内存位置,以便在运行程序时向其保存数据值)
特点:跳过所有空白字符后,从输入流中提取所需要的数据值,如果是char值,在输入单个字符后即停止输入;如果数据是int或者float,则在遇到第一个数据类型不合适的字符(比如空白字符)后停止数字的输入(后续会有深入问题见1.2.7)
1.2.4 C++的输入流与cin.get(变量)函数
istream数据的另一种读取字符数据的方法**,不会略过任何空白字符**
cin.get(someChar);
注意必须使用点标记法
1.2.4.1 应用之输入向量
#include<iostream>
#include<algorithm>
#include<vector>
using namespace std;
int main(){
vector<int> a;
int x;
while(cin>>x){
a.push_back(x);
if(cin.get()=='\n') break;
}
for(int i=0;i<a.size();i++) cout<<a[i]<<" ";
return 0;
}
1.2.5 C++的输入流与cin.ignore(number,someChar)函数
用于跳过(读取并丢弃)输入流的字符
cin.ignore(200,'\n')
跳过随后输入的200个字符,若在没结束之前遇到了换行符则终止
1.2.6 C++的输入流与getline(cin, myString)函数
常用来输入字符串
两种输入方法:
-
使用>>运算符——**跳过所有前导空白字符 **,然后将后续字符读入变量,并在第一个后缀空白字符处停止(这个后缀空白字符没有消失,而是作为在输入流中等待的第一个字符)
-
getline()方法——不会跳过空白字符,一直持续输入,直到遇到换行符号\n,并且换行符会被消去
#include<iostream>
#include<string>
using namespace std;
int main(){
string s1,s2;
char i,j,k;
getline(cin,s1);//输入字符串1
i=getchar();
cin>>s2;//输入字符串2
j=getchar();
k=cin.get();
cout<<"输出第一个字符:";
putchar(i);
cout<<endl<<"输出第二个字符串"<<s1<<endl<<"第一轮结束输出第二个字符:";
putchar(j);
cout<<endl<<"输出第二个字符串:"<<s2<<endl;
cout<<"第二轮结束输出第三个字符"<<k;
}
1.2.7 C++输入失败的情况
此处可以引申思考一个问题:
使用>>运算符输入其他类型的数据时,若前缀不匹配或者为空符号如何处理
答:若是空符号继续略过,若是不匹配的数据,cin流进入失败状态,即后续使用该流的IO操作都被认为是空操作,计算机继续执行程序但悄悄忽略所有使用这个流的后续尝试
补充:另一个使流进入失效状态的方法是尝试打开一个并不存在的输入文件;
1.3 常用math函数
#include<camth>
1.fab(double x)
2.floor(double x):向下取整
3.ceil(double x):向上取整
4.pow(double r,double p):返回r的p次方
5.sqrt(double x)
6.log(double x)
7.sin(double x)、cos(..)、tan(..)
8.asin(..)|acos(..)|atan(..)
9.round(double x):四舍五入,返回依旧double
1.5 语句结构
1.5.1 选择结构
if语句、switch语句
1.5.2 循环结构
while
do…while…
for
注意:C语言中不允许在for语句的表达式1中定义变量(int i的写法不允许),但在C++中可以
1.5.3 break与continue
2 数组
2.1 一维数组
初始化
int a[10] ={5,3,2,6,8,4};
int a[10] ={0};//整个数组都赋初值0
int a[10] ={};//同上
后面未被赋值的元素将会由不同编译器内部实现的不同而被赋以不同的初值(可能是很大的随机数),而一般情况默认初值为0
上面的情况部分未赋值时默认为0,但如果一开始没有赋初值,数组中每个元素都可能是一个随机数
2.2 二维数组
注意一点:如果数组大小较大(大概
1
0
6
10^6
106级别),需要将其定义在主函数外面,否则会使程序异常退出
原因:函数内部申请的局部变量来自系统栈,允许申请的空间较小,函数外申请的局部变量来自静态存储区,允许申请的空间较大
2.3 memset函数
按字节赋值,如对int型的四个字节就会被赋城相同的值,建议只用来赋值0或者-1(二进制补码一致)
include<cstirng>
memeset(数组名,值,sizeof(数组名));
2.4 字符数组
2.4.1 初始化
char str[15]={...};//初始化之一个一个赋值
char str[15]="Good!"//直接赋值,仅限于初始化可用
2.4.2 输入输出
1)%s通过空格或者换行来识别一个字符串的结束
scanf("%s",str);
printf("%s",str);
2)getchar 和 putchar 用于单个字符
str[i][j]=getchar();
putchar(str[i][j]);
3)gets(str) 与puts(str)
gets识别换行符\n(注意这里的换行符消失)结束,因此scanf一个整数后,如果使用gets,需要先用getchar接受整数后的换行符
puts输出一行字符串并且紧跟一个换行
2.4.3 存放方式
在一维字符数组(或者二维字符数组的第二维)的末尾都有一个空字符\0,以表示存放的字符串的结尾。空字符\0在使用gets或者scanf输入字符串时回自动添加在字符串后面,并占用一个字符位,而put与printf就是通过识别\0作为字符串的结尾来输出的
注意**:
- \0 ASCII码为0 空字符NULL 占用一个字节位 故开字符数组的时候记得字符数组的长度要比实际存储字符串的长度至少多1
- 如果不是使用scanf或者gets输入字符串,要手动在后面加上“\0”,否则printf和puts无法识别字符
2.4.5 sting.h头文件
1.strlen(str):得到字符数组第一个\0前的字符的个数,返回值int
2.strcmp(str1,str2):返回两个字符串大小的比较结果,原则为字典序,返回值为int
str1 < str2 返回负整数,不一定是-1
str1 = str2 返回0
str1 > str2 返回一个正整数,不一定是1
3.strcpy(str1,str2):把字符数组2赋值给字符数组1,包括了结束符\0,没有返回值
4.strcat(str1,str2):把一个字符串接到另一个字符串后面,没有返回值
5.sscanf(str, "%d", &n):把str中的内容以XX格式写到n中(从左往右)
复杂举例:
sscanf(str1,"%d:%lf,%s",&n,&db,str2);
char str[100] = "2048,3,14,hello“
6.sprintf(str,"%d",n);
3.函数
- 对计算机来说,main函数返回0的意·义在于告诉系统程序正常终止
- 以数组作为函数参数:
1)当数组作为参数时,参数中数组的第一维不需要填写长度(如果第二维需要填写长度),实际调用也只需要填写数组名
2)数组作为参数时,在函数中对数组元素的修改就等同于对原数组元素的修改
void change(int a[], int b[][5]){
...
}
4.指针
4.1 指针变量
- " * "的位置放在数据类型后或者变量名之前都可以
- 如果一次有好几个同种类型的指针变量用同时定义,星号只会结合于第一个变量名
- 记住,星号是类型的一部分
int* p1,p2;
前一个是指针类型,后一个是整型
int *p1,*p2;
int *p1=&a,*p2=&b;
scanf("%d",a+i);//输入数组元素的新写法
print("%d",&(a+i));
- 指针变量支持加减法、自增自减
- 对指针变量来说,其存储的地址的类型称为基类型
- p是指针的地址,*p是这个地址存放的元素
4.2 指针与数组
- 数组名称可作为数组首地址使用
- 地址间的距离以基本类型为单位
4.3 值传递与地址传递
指针类型作为函数参数的类型,如果在函数中对这个地址中的元素进行改变,原先的数据就会确实地被改变
值传递:向函数传递一个副本,原本的值不会改变
地址传递:只有在获取地址的情况下对元素进行操作,才能真正地修改变量
易错:
- 定义指针变量temp时,若没有初始化,其存放的地址死后随机的,如果指向了系统工作区间就会出错
- 地址传递本身传入的也只是地址这个无符号整型的数,对地址本身的修改并不能影响地址本身*——解决方法:指针引用(见下)
4.4 引用
不产生副本,给原变量起了个别名,对引用变量的操作就是对原变量的操作
- 在参数类型后面加上&,一般写在变量名前面
- 不管是否使用引用,函数的参数名和实际传入的参数名可以不同
- 常量不可使用引用
5 结构体
struct node{
node n;//不可以
node* next;//可以,表示只想下一个元素的指针
}a,b,c[1000];
- 结构体里面不能定义自己本身,但可以定义自身类型的指针变量
- 结构体指针变量的访问
struct studentInfo{
int id;
char name[20];
studentInfo* Next;
}stu, *p; 定义了结构体变量与结构体指针变量
stu.id
stu.name
stu.next
(*p).id 注意 . 的优先级比 * 高,要加括号
优化方法:
p->id
- 结构体的初始化
对一个普通定义的结构体,其内部回生成一个默认的构造函数(但不可见),其函数名与结构体类型名相同
struct student{
int id;
char gender;
student(){}
};
有了这个构造函数的存在
才可以直接定义结构体类型的变量而不进行初始化(因为它没有让用户提供任何初始化参数)
struct student{
int id;
char gender;
student(int _id, char _gender){
id = _id;
gender = _gender;
}
};
struct student{
int id;
char gender;
student(int _id, char _gender):id(_id),gender(_gender){}
};
如果自己重新定义了构造函数,则不能不经初始化就定义结构体变量。默认构造函数被覆盖了
student = student(10086,'M');
可以定义任意多个构造函数,以适应不同的初始化场合
6 补充
6.1 浮点数的比较(略)
6.2 复杂度(略)
6.3 黑盒测试
6.3.1 单点测试
只需要按正常的逻辑执行一遍程序
6.3.2 多点测试
要求程序能够一次运行所有的数据,并要求输出结果必须完全正确才算通过
- while…EOF型
当题目没有给定输入的结束条件,就默认读到文件末尾
scanf函数的返回值为其成功读入的参数个数,正常的控制台的输入一般不会读取失败,只有读取文件时到达末尾产生无法读取现象,才会产生读入失败。这是,scanf函数返回-1而不是0 ,且C语言中使用EOF来代表-1;
while(scanf("%s",str) != EOF){
...
}
while(gets(str) != NULL){
...
}
- while…break型
题目要求输入的数据满足某个条件时停止输入
- 一种时在上一种的内部进行判断
- 一种是将判断条件放进while语句,与scanf语句用逗号隔开
while(scanf("%d%d",&a,&b),a||b){} ——当a和b有一个不为零时就进行循环
- while(T–)型
题目给出测试的组数
int T;
scanf("%d",&T);
while(T--){}