北航刷题笔记 其二

北航刷题知识点整集2025.2.2:

1.printf的转义字符:

在输出一些字符的时候,某些字符是系统默认识别的,需要在前面加上特定的字符才能输出

如:

\ “ ? 这些前面要加上\才能正常输出

**%的前面要再加一个%**才能输出

2.int类型的数做除法运算时的取整:

一般都是向下取整,即向0取整

如果想要向上取整,通常用的公式是(a+b-1)/b

1
//示例:(5 + 3 -1)/3 = 7/3=2(5/3向上取整为2)

要注意负数的情况

3.数据类型和格式符:

1.赋值时如果等号两端是不同的数据类型,那么右边的数据类型在运算结束时会自动向左边的数据类型转换

2.如果在运算的过程中有多个数据类型,它会由下图中的顺序由低往高进行转换:

预览大图

先是整数,由短到长,长到极限,再是浮点,浮点之间,单独做比

如:算术表达式中普通整数( int )无符号整数( unsigned )混合使用,则普通整数将自动转换成无符号整数

隐式转换陷阱:如unsigned intint运算时,负数会转为大正数。

1
2
3
int a = -1;
unsigned int b = 1;
if (a > b) // 成立,因为a被转为4294967295(32位系统)

显式转换:强制类型转换(type)var(**(double)a/b**)。

3.数据类型中,格式符都不同:

1
2
3
4
5
6
%d----int
%lld----long long int
%lf----double
%f----float
%llu----unsigned long long
%u----unsigned

4.宏定义和const常量

1.宏定义define:

define后不能接分号;

define在程序中的生命周期停于编译阶段,是纯粹的文字替换编译器不对其进行类型的检查(即当编译时,首先编译器会将程序里面的需要替换的同一替换再进行编译)

注意:带宏的副作用:

1
2
3
#define SQUARE(x) x*x
SQUARE(3+2); // 展开为3+2*3+2=11,而非25
// 正确写法:#define SQUARE(x) ((x)*(x))

2.const:

const可以放于任何变量定义之前,包括指针变量也是;

const定义的常数是变量,还是有数据类型的,编译器仍然会对其进行分配空间和类型的安全检查

5.编程技巧:提取一个数字的每一位:(充分利用%,/运算):

1
2
3
4
while(k>0){
int a=k%10;//每一位
k/=10;
}

6.ASCLL码:

首先,对于字符串来说,ascll码的运算是直接加数字的,如果是**%c就输出ascll码的代表值,如果是%d就输出ascll码的数字值平时程序里面的运算和比较都是在ascll码的数字值的比较。因为:字符的本质是一个整数**

下面是一些常用的ascll码(大写字母和小写字符相差32):

1
2
3
4
5
'0'-'9'------48-57
'A'-'Z'------65-90
'a'-'z'------97-122
'\n'---------10
' '----------32//如果实在不记得可以直接用字符运算

可以通过ascll码之间的运算来实现字符与非字符之间的转换:

3=’3’-‘0’

7.编程技巧:输入输出优化:

c/c++中用scanf和printf比cin和cout的速度要快些

在c++中还能用关闭同步流的方法:

1
2
ios::sync_with_stdio(false);
cin.tie(0);

8.编程技巧:闰年计算

能被4整除但不能被100整除 或者 能被400整除

9.编程技巧:日期,星期几的计算

0为周日,1-6为星期一到星期六

吉姆拉尔森公式:根据给出的年月日,计算星期几—————-注意:公式中一月和2月当做上一年的13和14月,所以要单独判断月+12,年-1

1
2
3
4
5
6
7
8
9
10
int get_weekday(int y, int m, int d) {
if (m < 3) {
m += 12;
y--;
}
int c = y / 100; // 世纪
y = y % 100; // 年份后两位
int w = (d+2*m+3*(m+1)/5 + y + y/4 - y/100 + y/400) % 7;//(天+2*月+3*(月+1)/5+年+年/4-年/100+年/400)模7
return (w + 1) % 7; // 转换为0=周日
}

10.编程技巧:最大公约数:欧几里得算法(辗转相除法)

递归实现:就是看b通过循环一直对ab交换,b=a%b,a=b,最后看如果b=0的话,那么a就是最大公约数

1
2
3
int gcd(int x,int y){
return y=0?x:gcd(y,x%y);
}

最小公倍数:x*y/最大公约数

11.scanf的输入格式:(读取空格自动停止)

scanf输入时要严格按照输入的格式来,不能额外有什么符号

注意:有时候scanf会把前面的换行符也算一个符号,所以看情况空格

若格式字符串中有非格式符(如逗号),输入必须包含相同符号。

12.fgets输入函数(中途遇到空格不会停止)

1
fgets(s,sizeof(s),stdin);//注意:fgets函数会自动保留换行符,如果输入hello,保存的是hello\0

13.编程技巧:矩形嵌套判断*

当且仅当:a的长>b的长a的宽>b的宽

1
2
3
int can_nest(Rect a, Rect b) {
return (a.short < b.short) && (a.long < b.long);
}//比较函数,位于stdlib.h的qsort函数

14.qsort函数—stdlib.h

- 如果返回值 < 0,表示第一个参数应该排在第二个参数前面。

- 如果返回值 == 0,表示两者相等。

- 如果返回值 > 0,表示第一个参数应该排在第二个参数后面。

根据符号来记忆,符号开口方向为前面不完全又正负决定升降序

15.浮点数比较误差:fabs()函数—math.h

1**.fabs()函数输出浮点数绝对值**

2.当进行浮点数比较的时候,因为精度问题,会有一定误差,这时,可以看他们是否<1e9或1e6

3.abs()—stdlib.h这个函数是用于输出整数的绝对值

16.格式的补零(常用于输入补0操作)

以**%09.2f**为例

1
2
3
4
5
%--格式说明符的起始标志
0--如果没有满足要求的位置,剩余位数自动补0,当然这里可以为其他数字//从左侧开始补0
9--要求的位数//如果超过就照常输出
.2--小数部分精度
f--浮点数

17.角度的计算:atan()函数和atan2()函数—math.h

1
2
3
4
5
6
atan(tana)//先通过这个函数得出弧度
//参数是tan(a)
//最后转换成角度=弧度*(180/π)
atan2(y,x)//这个函数能根据想x,y处于哪个象限返回正确的角度
//在c语言中范围是[-π,π]
//返回的是以x轴正方向为起点,到想x,y的极角

18.编程技巧:同余

如果:

1
2
3
4
a%m=c
b%m=c
//那么一定:
(a-b)%m=0

这就是同余,两个数同时余一个数的余数相同那么两数之差一定是这个数的倍数

在实际中,可能要统计余数相同的次数,每个次数相同的余数都可以进行同余

19.编程技巧:前缀和和二维前缀和

前缀和:

1
2
3
4
5
int a[n];//原数组,假设已经赋值了
int b[n+1];//前缀和数组,n+1是因为b[0]要储存为0,这样计算中间前缀和的时候不会漏
for(int i=1;i<n+1;++i){
b[i]=b[i-1]+a[i-1]//这里是当前数据和之前数据之和,因为b比a大1,所以后面2个是相同对的,可以在脑子里想一想同一起跑线的
}

二维前缀和:

1
2
3
b[m][n]=b[m-1][n]+b[m][n-1]-b[m-1][n-1]+a[m][n]//自己在脑海中想一想
//中间的前缀和也差不多
s=b[m][n]-b[m-1][n]-b[n-1][m]+b[m-1][n-1];//还是图形想象,图形的加减

20.用于最值的初始化比较—limits.h

最大值INT_MAX

最小值INT_MIN

21.编程技巧:n的因数查找

找一个数n的的因数时,最大范围可以设置为**sqrt(n)**即根号n

22.直线外一点到直线的距离:

直线外一点到直线的距离公式:
$$
点 P(x0,y0) 到直线 Ax+By+C=0的距离 D 的计算公式为:
$$

$$
D = \frac{|Ax_0 + By_0 + C|}{\sqrt{A^2 + B^2}}
$$

23.编程技巧:二进制的转换以及n进制的转换

1.二进制中从右到左的权值是2^0-n^开始的

2.10进制转n进制:充分利用好/%,就是在while中不断地取余数,每次更新num/=n的商,存入数组中,最后倒序输出

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
//将十进制数转换为n进制
void decimalToBaseN(int num, int base) {
if (num == 0) {
printf("0");
return;
}
char result[32]; //存储转换结果的数组
int index = 0; //结果数组的索引
while (num > 0) {
int remainder = num % base; // 取余数
if (remainder < 10) {
result[index++] = remainder + '0'; // 数字直接转换为字符
} else {
result[index++] = remainder - 10 + 'A'; // 字母转换(如:A、B、C...)
}
num /= base; // 将num更新为商
}
//输出结果时,需要倒序打印
for (int i = index - 1; i >= 0; i--) {
printf("%c", result[i]);
}
}

24.编程技巧:汉诺塔的移动过程以及移动次数

1.移动次数:

因为是递归的基础上,将其想成n个小问题

假如有n个盘子,n盘最下面

这时,对于n这一个和n-1那一堆:

1.先n-1移动到B:H(n-1)//这是次数

2.再n这单个盘子移动到C:1

3.最后n-1这堆盘子移动到C:H(n-1)

即:H(n)=2*H(n-1)+1

具体的规则可能根据题目的变化而变化

2.移动过程

1
2
3
4
5
6
7
8
9
10
11
12
13
void hanoi(int n,char a,char b,char c){
if(n==1){
printf("%d form %c to %c",n,a,c);//n=1直接从a柱到c柱
}
else{
//先将n-1个盘子从原柱移动到辅助柱子
hanoi(n-1,a,c,b);
//然后将第n个盘子从源柱移动到目标柱子
printf("%d from %c to %c\n",n,a,c);
//最后将n-1个盘子从辅助柱移动到目标柱子
hanoi(n-1,b,a,c);
}
}//对于三个位置的从哪移动到哪的方位记在心里就好

25.动态内存管理:

1.对数组的动态内存分配:(未初始化的情况下)

1
int *arr=(int*)malloc(n*sizeof(int))

2.对数组的动态内存分配并初始化为0

1
int *arr=(int*)calloc(n,sizeof(int))//注意:这有2个参数,上面只有一个

3.调整已经分配内存的数组的大小:

1
arr=(int*)realloc(arr,new_size*sizeof(int))//new_size是新的内存大小

4.释放内存:

1
2
free(arr);
arr=NULL;//这一步是为了避免悬空指针

26.编程技巧:全错位排序

d(0)=1

d(1)=0

**d(n)=(n-1)d(n-1) d(n-2)

27.~取反运算符

~EOF=0

~1=-1

对操作数取反

常用于:while(~scanf(“%d”,&a))这种单输入的while循环

28.自然常数e的表示—math.h

exp(1)

29.三次方根和二次方根—math.h

sqrt(n)—二次

cbrt(n)—三次

30.编程技巧:求不规则函数的面积:

能被积分,且知道不规则函数的方程

辛普森法则:
$$
\int_{a}^{b} f(x) , dx \approx \frac{b - a}{6} \left( f(a) + 4f\left(\frac{a + b}{2}\right) + f(b) \right)
$$
不追求精确度可以用梯形的方式计算

31.编程技巧:地图方向问题

可以定义2个想x,y数组来控制方向:

1
2
x[]={-1,-1,-1,0,0,1,1,1};
y[]={1,0,-1,1,-1,1,0,-1};

32.string头文件中对字符串的操作函数:

1.安全比较2个字符串的前n个字符串,并根据他们的字典中排序返回他们的差异:

1
2
3
4
strncmp(str1,str2,n);//函数原型:int strncmp(const char *str1, const char *str2, size_t n);
//如果str1的前n个字符等于str2就返回0
//如果前n个字符的字符字典序要大,就返回正数,反之负数
//大正

2.返回字符串的长度

1
strlen(str1);

3.安全复制字符串前n个字符

1
2
strncpy(dest,str1,n)//将str1的前n个字符串复制到dest空字符串中
//函数原型:char *strncpy(char *dest, const char *src, size_t n);

4.查找字符出现的首位置:(返回的是地址)

1
strchr(str1,'c')//原型:char *strchr(const char *str, int c);

5.查找字符子串在字符串中出现的首位置:(返回的是地址)

1
strstr(str1,str2)//原型char *strstr(const char *haystack, const char *needle);

6.分割字符串:

1
2
3
strtok(str,t);//根据t把字符串分割为一段一段的
//注意:其返回的不是数组,而是指针,本质是替换为'\0',可以用循环来获取切割后的
//如果要存入数组,后续手动存

33.判断字符字母是大写还是小写—ctype.h

1
2
3
isupper()//判断大写字母
islower()//判断小写字母
//记不住可以直接比较ascll码也行

34.取余%操作

取余的话会一直取到底,注意。

35.编程技巧:约瑟夫问题数学方法求解

下面是直接求出最后一人出局的初始位置

1
2
3
4
5
6
7
int yuesefu(int n,int m){
int res=0;
for(int i=0;i<=n;++i){
res=(res+m)%i;
}
return res+1;
}

36.编程技巧:计算一个已知各顶点的多边形面积:

叉积法:
$$
\text{面积} = \frac{1}{2} \times \left| \sum_{i=1}^{n} (x_i \cdot y_{i+1} - x_{i+1} \cdot y_i) \right|
$$
就是二分之一×两数交叉之差绝对值

37.结构体排序:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
//这是多条件的,具体qsort用法引索
int compareStudents(const void *a, const void *b) {
Student *sa = (Student *)a; // 后面的部分是将 void 指针类型 强制转换 为 Student 指针类型
Student *sb = (Student *)b;
// 按月份升序排序
if (sa->month != sb->month)
return sa->month - sb->month;
// 如果月份相同,按日期升序排序
if (sa->day != sb->day)
return sa->day - sb->day;

// 如果日期也相同,按姓名字典序升序排序
return strcmp(sa->name, sb->name);
}

38.编程技巧:田忌赛马策略

1.先看我方最大是否大于敌方最大,并且我方最小是否大于敌方最小

2.如果不是,则用我方最小去战对方最大