字符串
定义
char a[101010];
初始化
char a[10100] = "miao";
输入输出
char a[100010];
scanf("%s", a);
printf("%s", a);
字符串函数
前提
#include <string.h>
strlen
char a[10010] = "hello";
long long len = strlen(a);
strcpy
char a[10010], b[10010];
strcpy(a, b); // b -> a
strcmp
- -1: s t r 1 < s t r 2 str1 < str2 str1<str2
- 0: s t r 1 = s t r 2 str1 = str2 str1=str2
- 1: s t r 1 > s t r 2 str1 > str2 str1>str2
char a[10010], b[10010];
int cmp = strcmp(a, b);
strchr
在字符串中寻找指定的字符,返回一个字符指针指向第一个匹配的字符
char* strchr(char*, int);
char a[10010] = "miao";
char* p = strchr(a, 'm'); // *p = m
char* p2 = strchr(a, 'p'); // p2 = NULL
strstr
char* strstr(char*, char*);
char a[10010] = "miao";
char* p = strstr(a, "m");
strcat
连接,将str2连接到str1的末尾
void strcat(char*, char*);
char a[10010], b[10010];
strcat(a, b);
sprintf
char a[10010],c[10010];
int b;
sprintf(a, "%d%s%c", b, c, 'e');
字符串输入输出函数
getchar
功能
从缓冲区中读取一个字符
函数原型
char getchar();
使用
char a;
a = getchar();
与
scanf("%c", &a);
的功能一模一样
putchar
功能
输出一个字符
函数原型
int putchar(int);
与
printf("%c", a);
功能一模一样
puts
功能
输出一个字符串,自带一个换行符
puts("adawdfawadf");
gets
功能
读入一个字符串
使用
char a[10001];
gets(a);
顺序结构
数据类型
基本
整数类型
char
1 %c
short
2 %s
int
4 %d
1
0
9
10^9
109
long
8 %ld
long long
8 %lld
小写变大写
小写的ascii码比大写大32
a - 32
c - ‘0’
浮点类型
float
4 %f
double
8 %lf
long double
16 %Lf
类型转换
隐式类型转换 --> 自动转换 --> 小的和大的做运算的时候,小的会自动转换成大的进行运算
int a, b;
double c;
a / c; // 先把 a 转换成double类型,在进行运算
a / b; // 整除
1.0 * a / b; // 先把 a 和 b 转换成double类型,在进行运算
int k1 = 1e9, k2 = 1e9;
long long f = k1 * k2; // 在运算过程中溢出
long long g = 1LL * k1 * k2; // 不会溢出
int a = g; // 自动截断
显示类型转换 -> 强制类型转换 -> 强转
int a, b;
double c;
a / b; // 整除
(double) a / b; // 先把 a 强转成double,然后 b会隐式类型转换成double,然后做运算
// 先算然后再转换
(double) (a / b) // 先整除,然后把整除的结果转换成 double
核心
- 小的转成大的
- 是否整除、避免运算过程中溢出
存储类
auto
默认就是auto
int a;
auto int a;
在{}开始时创建这个变量,结束时销毁这个变量
实例
#include <stdio.h>
int main() {
for (int i = 1; i <= 3; ++i) {
int a = 0;
a += i;
printf("%d\n", a);
}
return 0;
}
E:\untitled3\cmake-build-debug\untitled3.exe
1
2
3
Process finished with exit code 0
register
寄存器
static
- 不会被销毁掉
- 只会定义和初始化一次
实例
#include <stdio.h>
int main() {
for (int i = 1; i <= 3; ++i) {
static int a = 0;
a += i;
printf("%d\n", a);
}
return 0;
}
E:\untitled3\cmake-build-debug\untitled3.exe
1
3
6
Process finished with exit code 0
运算符
算术
运算符
+、-、*、/、%
++, –
int a = 10;
printf("%d", a++); //10
printf("%d", ++a); //11
实例
#include <stdio.h>
int main() {
int a = 21;
int b = 10;
int c;
c = a + b;
printf("Line 1 - c 的值是 %d\n", c); // 31
c = a - b;
printf("Line 2 - c 的值是 %d\n", c); // 11
c = a * b;
printf("Line 3 - c 的值是 %d\n", c); // 210
c = a / b;
printf("Line 4 - c 的值是 %d\n", c); // 2
c = a % b;
printf("Line 5 - c 的值是 %d\n", c); // 1
c = a++; // 赋值后再加 1 ,c 为 21,a 为 22
printf("Line 6 - c 的值是 %d\n", c);
c = a--; // 赋值后再减 1 ,c 为 22 ,a 为 21
printf("Line 7 - c 的值是 %d\n", c);
}
关系
运算符
==、!=、<=、>=、<、>
关系运算符得到的结果要么是0要么是1
实例
#include <stdio.h>
int main() {
int a = 21;
int b = 10;
int c;
if (a == b) {
printf("Line 1 - a 等于 b\n");
} else {
printf("Line 1 - a 不等于 b\n");
}
if (a < b) {
printf("Line 2 - a 小于 b\n");
} else {
printf("Line 2 - a 不小于 b\n");
}
if (a > b) {
printf("Line 3 - a 大于 b\n");
} else {
printf("Line 3 - a 不大于 b\n");
}
/* 改变 a 和 b 的值 */
a = 5;
b = 20;
if (a <= b) {
printf("Line 4 - a 小于或等于 b\n");
}
if (b >= a) {
printf("Line 5 - b 大于或等于 a\n");
}
}
逻辑
运算符
&& 且/与 --> 1 && 1 = 1,只有同时为真结果才为真,与 --> “一起”
|| 或 --> 0 || 0 = 0,只要有一个真结果就是真,或 --> 一个人行就行
! 非/取反 !1 = 0,!0 = 1
//c语言中表达式的值非0,在逻辑运算中当作是真
int a = -5;
int b = 5;
!a; //0
!!a; //1
!(a + b); //1
!a + b; //5
实例
#include <stdio.h>
int main() {
int a = 5;
int b = 20;
int c;
if (a && b) {
printf("Line 1 - 条件为真\n");
}
if (a || b) {
printf("Line 2 - 条件为真\n");
}
/* 改变 a 和 b 的值 */
a = 0;
b = 10;
if (a && b) {
printf("Line 3 - 条件为真\n");
} else {
printf("Line 3 - 条件为假\n");
}
if (!(a && b)) {
printf("Line 4 - 条件为真\n");
}
}
位运算
跟逻辑运算的区别
位运算是对于两个数而言的,结果是一个数,两个数做位运算是对每一个二进制位分别做位运算
逻辑运算是对于真/假而言的,结果是真/假,只不过c语言在做逻辑运算的时候把非0数当成真,把0当成假
运算符
&: 与 1 & 1 = 1
|: 或 0 | 0 = 0
^: 异或 -> “不进位的加法” 0 ^ 1 = 1, 0 ^ 0 = 0, 1 ^ 1 = 0
异或性质:
- a ⊕ b = b ⊕ a a \oplus b = b \oplus a a⊕b=b⊕a
- a ⊕ a = 0 ⇒ b ⊕ a ⊕ a = b a \oplus a = 0 \Rightarrow b \oplus a \oplus a = b a⊕a=0⇒b⊕a⊕a=b
由性质2可得一个模板
// 交换两个数
int a, b;
a = a ^ b; // a = a ^ b, b = b;
b = a ^ b; // a = a ^ b, b = a;
a = a ^ b; // a = b, b = a;
// 模板
a = a ^ b, b = a ^ b, a = a ^ b;
<<: 左移->整体向左移,超过的丢掉-----丢掉的内容和存储长度有关
// 判断 n 的第 i 位是不是1
if (n & (1 << (i - 1))) printf("n的第i位是1");
// 判断 n 是不是奇数
// 判断 n 的第 1 位是不是1
if (n & 1) printf("n是奇数");
>>: 右移->整体向右移,超过的丢掉------丢掉的内容和存储长度无关
// 把 n 除以2 向下取整, 5 >> 1 = 2
n >> 1;
// 把 n 除以 2^i 向下取整
n >> i;
~: 取反-> 所有二进制位0变1,1变0
实例
#include <stdio.h>
int main()
{
unsigned int a = 60; /* 60 = 0011 1100 */
unsigned int b = 13; /* 13 = 0000 1101 */
int c = 0;
c = a & b; /* 12 = 0000 1100 */
printf("Line 1 - c 的值是 %d\n", c );
c = a | b; /* 61 = 0011 1101 */
printf("Line 2 - c 的值是 %d\n", c );
c = a ^ b; /* 49 = 0011 0001 */
printf("Line 3 - c 的值是 %d\n", c );
c = ~a; /*-61 = 1100 0011 */
printf("Line 4 - c 的值是 %d\n", c );
c = a << 2; /* 240 = 1111 0000 */
printf("Line 5 - c 的值是 %d\n", c );
c = a >> 2; /* 15 = 0000 1111 */
printf("Line 6 - c 的值是 %d\n", c );
}
赋值
运算符
=
a += b -> a = a + b;
a -= b -> a = a - b;
a *= b -> a = a * b;
a /= b -> a = a / b;
a %= b -> a = a % b;
a <<= b -> a = a << b;
a >>= b -> a = a >> b;
a &= b -> a = a & b;
a |= b -> a = a | b;
a ^= b -> a = a ^ b;
实例
#include <stdio.h>
int main()
{
int a = 21;
int c ;
c = a;
printf("Line 1 - = 运算符实例,c 的值 = %d\n", c );
c += a;
printf("Line 2 - += 运算符实例,c 的值 = %d\n", c );
c -= a;
printf("Line 3 - -= 运算符实例,c 的值 = %d\n", c );
c *= a;
printf("Line 4 - *= 运算符实例,c 的值 = %d\n", c );
c /= a;
printf("Line 5 - /= 运算符实例,c 的值 = %d\n", c );
c = 200;
c %= a;
printf("Line 6 - %%= 运算符实例,c 的值 = %d\n", c );
c <<= 2;
printf("Line 7 - <<= 运算符实例,c 的值 = %d\n", c );
c >>= 2;
printf("Line 8 - >>= 运算符实例,c 的值 = %d\n", c );
c &= 2;
printf("Line 9 - &= 运算符实例,c 的值 = %d\n", c );
c ^= 2;
printf("Line 10 - ^= 运算符实例,c 的值 = %d\n", c );
c |= 2;
printf("Line 11 - |= 运算符实例,c 的值 = %d\n", c );
}
特殊
运算符
-
sizeof():返回变量所占内存空间大小,单位:字节(Byte)
-
&:取地址,返回变量的地址,表示变量在内存中的位置,十六进制数
-
*:取值,返回地址中的内容
-
逻辑表达式?值1:值2 :逻辑表达式为真,整个表达式的值是值1
逻辑表达式为假,整个表达式的值为值2
printf("%d", 1 > 2 ? 10 : 20); // 20
实例
#include <stdio.h>
int main() {
int a = 4;
short b;
double c;
// 定义了一个指向int类型的指针 ptr
int *ptr;
/* sizeof 运算符实例 */
printf("Line 1 - 变量 a 的大小 = %lu\n", sizeof(a));
printf("Line 2 - 变量 b 的大小 = %lu\n", sizeof(b));
printf("Line 3 - 变量 c 的大小 = %lu\n", sizeof(c));
/* & 和 * 运算符实例 */
// ptr 存放a的地址
ptr = &a; /* 'ptr' 现在包含 'a' 的地址 */
printf("a 的值是 %d\n", a);
printf("*ptr 是 %d\n", *ptr);
/* 三元运算符实例 */
a = 10;
int d = (a == 1) ? 20 : 30;
ptr = &d;
printf("ptr 的值是 %d\n", *ptr);
b = (a == 1) ? 20 : 30;
printf("b 的值是 %d\n", b);
b = (a == 10) ? 20 : 30;
printf("b 的值是 %d\n", b);
}
优先级
完全不需要记,除了下面几个事
-
位运算优先级特别特别低,涉及到位运算,尽量加括号
a & b && c; // a & (b && c)
-
逻辑运算中,或运算比与运算优先级低,两边有与运算中间是或运算,先算两边的与运算
a && b || c && d; // (a && b) || (c && d)
总结
算数: 四则运算、%、自增自减
关系: 大于小于等于不等于
逻辑:与或非
位运算:与或异或取反移位
赋值:=,+=
杂项:sizeof,&,*,?:
选择结构
C 语言把任何非零和非空的值假定为true,把零或null 假定为 false
循环结构
函数
执行流程
- 保护现场,临时变量记录实参的值
- 将临时变量的值赋值给形参
- 跳转到被调函数执行
- 临时变量保存return的值
- 恢复现场,继续执行调用函数
理解
黑盒子
答案类型 函数名(输入){
处理
return 答案
}
一个函数可以有多个输入,但只能有一个输出
定义
返回值类型 函数名(形参列表) {
语句
return 值;
}
调用
黑盒子名字(输入的内容);
函数名(实参列表);
参数传递方式
值传递
把临时变量中的值赋值给形参
函数嵌套
函数中调用其它函数
执行顺序:执行完被调函数后返回调用函数
#include <stdio.h>
int f(int x) {
return x * x + 1;
}
int SunFun(int n) {
int s = 0;
for (int x = 0; x <= n; ++x)
s += f(x);
return s;
}
int main() {
printf("%d", SunFun(2));
return 0;
}
函数声明
返回值 函数名(形参列表);
由于c必须要先声明再使用,使用函数之前要么声明要么定义出这个函数
递归调用
函数自己调用自己
如果没有出口函数,会死循环
void f(int x) {
printf("%d\n", x);
f(x + 1);
}
计算阶乘
#include <stdio.h>
long long f(int n) {
if (n == 0 || n == 1)
return 1;
else
return f(n - 1) * n;
}
int main() {
int n;
long long y;
scanf("%d", &n);
y = f(n);
printf("%d %lld", n, y);
return 0;
}
#include <stdio.h>
// f[i]表示i!
int f[10000010];
int main() {
int n;
scanf("%d", &n);
f[1] = 1;
for (int i = 2; i <= n; ++i)
f[i] = f[i - 1] * i;
for (int i = 1; i <= n; ++i)
printf("%d ", f[i]);
return 0;
}
求斐波拉契数列
设计一个递归
f ( n ) = f ( n − 1 ) + f ( n − 2 ) n > = 3 f(n) = f(n - 1) + f(n - 2)\;\;\;n >= 3 f(n)=f(n−1)+f(n−2)n>=3
f ( 1 ) = f ( 2 ) = 1 f(1) = f(2) = 1 f(1)=f(2)=1
#include <stdio.h>
int f(int x) {
if (x == 1 || x == 2)
return 1;
else
return f(x - 1) + f(x - 2);
}
int main() {
int n;
scanf("%d", &n);
printf("%d", f(n));
return 0;
}
#include <stdio.h>
// f[i]表示斐波拉契第i项
int f[10000010];
int main() {
int n;
scanf("%d", &n);
f[1] = f[2] = 1;
for (int i = 3; i <= n; ++i)
f[i] = f[i - 1] + f[i - 2];
for (int i = 1; i <= n; ++i)
printf("%d ", f[i]);
return 0;
}
指针
概念
指针变量的值就是变量的地址
定义
int a;
// 把a的地址存到p里边,称作p指向a
int* p = &a;
作为函数参数传递
void swap(int* a, int* b) {
int t = *a;
*a = *b;
*b = t;
}
int main() {
int a = 10, b = 20;
swap(&a, &b);
printf("%d %d", a, b);
return 0;
}
程序分析
说一下代码运行过程和结果
时间复杂度
标志一个算法的运行时间跟输入规模的关系
O ( n ) , O ( n 2 ) , O ( l o g n ) , O ( 2 n ) , O ( n ! ) O(n), O(n ^ 2), O(logn), O(2^n), O(n!) O(n),O(n2),O(logn),O(2n),O(n!)、
空间复杂度
没开数组,只用变量 O ( 1 ) O(1) O(1)
开了数组, O ( n ) O(n) O(n)
开了二维数组, O ( n 2 ) O(n^2) O(n2)
模板
gcd(最大公约数)
欧几里得算法
int gcd(int a, int b) { return b ? gcd(b, a % b) : a; }
读入数组
int n;
scanf("%d", &n);
for (int i = 1; i <= n; ++i) scanf("%d", &a[i]);
最值
// mini 代表最值所在的位置, 如果当前元素比最值还要小,更新这个值
int mini = 1; // 最小值所在的下标
for (int i = 2; i <= n; ++i)
if (a[i] < a[mini]) mini = i;
// 如果有多个值相等,输出的下标会是第一个最值
模拟
取模
循环中一直取模可以"限制变量的取值范围[0, mod - 1]"
取模的作用之一:得到的结果永远不会大于模数
题目说什么你做什么
// cur 表示当前的状态,遍历所有情况,更新cur,算答案
cur从x开始增加,但凡cur增加到7,就让cur变成0
但凡-暴力
遍历一遍,但凡满足条件,更新答案
- 一个变量记录要维护的信息,即“可以通过这些信息直接算出答案的信息”
- 遍历----》遍历的内容、退出循环的条件
- 如何维护信息
冒泡
思路
- 每次比较相邻元素,将大的放到右边
- 这样一轮比较下来,最大的元素就被移动到了数组的最右边
- 比较 n − 1 n - 1 n−1 轮,就排好序了
代码
0base
int a[100010], n;
int main() {
scanf("%d", &n);
for (int i = 0; i < n; ++i) scanf("%d", &a[i]);
for (int i = 1; i < n; ++i)
for (int j = 0; j < n - i; ++j)
if (a[j] > a[j + 1]) {
int tmp = a[j];
a[j] = a[j + 1];
a[j + 1] = tmp;
}
for (int i = 0; i < n; i++) printf("%d ", a[i]);
return 0;
}
1base
int a[100010];
int n;
int main() {
memset(a, 128, sizeof(a));
scanf("%d", &n);
for (int i = 1; i <= n; i++) scanf("%d", &a[i]);
for (int i = 1; i <= n; i++)
for (int j = 1; j <= n; j++)
if (a[j] > a[j + 1]) {
int tmp = a[j];
a[j] = a[j + 1];
a[j + 1] = tmp;
}
}
算法分析
时间复杂度
O ( n 2 ) O(n^2) O(n2)
空间复杂度
O ( n ) O(n) O(n)
选择
思路
每次从未排序的地方选择最小值,放到已排序的最后一个位置
举例:
3 4 2 1
-
从后 4 4 4 个数中找到最小值 1 1 1,放到第一个位置 1 , 3 , 4 , 2 1,3, 4, 2 1,3,4,2
-
从后 3 3 3 个数中找到最小值 2 2 2,放到第二个位置 1 , 2 , 3 , 4 1,2,3,4 1,2,3,4
-
从后 2 2 2 个数中找到最小值 3 3 3,放到第三个位置 1 , 2 , 3 , 4 1,2,3,4 1,2,3,4
总结
- 循环 n − 1 n - 1 n−1 次
- 第 i i i 次循环中,从 i − 1 ∼ n − 1 i - 1 \sim n - 1 i−1∼n−1 中找到最小值,放到第 i − 1 i - 1 i−1 个位置
记忆
- 循环
- 在第 i i i 次循环中,从一个区间中找到最小值,放到正确的位置
代码
0base
#include <stdio.h>
int a[100010], n;
int main() {
scanf("%d", &n);
for (int i = 0; i < n; ++i)
scanf("%d", &a[i]);
for (int i = 1; i <= n - 1; ++i) {
int mini = i - 1;
for (int j = i; j <= n - 1; ++j)
if (a[j] < a[mini]) mini = j;
int tmp = a[i - 1];
a[i - 1] = a[mini];
a[mini] = tmp;
}
for (int i = 0; i < n; ++i)
printf("%d ", a[i]);
return 0;
}
1base
- 循环 n − 1 n - 1 n−1 次
- 第 i i i 次循环,从 [ i , n [i, n [i,n] 找到最小值,放到 i i i
scanf("%d", &n);
for (int i = 1; i <= n; ++i)
scanf("%d", &a[i]);
for (int i = 1; i <= n - 1; ++i) {
int mini = i;
for (int j = i; j <= n; ++j)
if (a[j] < a[mini]) mini = j;
int tmp = a[i];
a[i] = a[mini];
a[mini] = tmp;
}
插入
思路
每次从待排序的地方拿出第一个元素,插入已排序的序列中的正确的位置上去
举例:
3 2 1 4
- 拿出2,跟3比,比3小,放到3左边, 2 , 3 , 1 , 4 2, 3, 1, 4 2,3,1,4
- 拿出1,跟3比,比3小,交换1和3,跟2比,比2小,交换1和2, 1 , 2 , 3 , 4 1,2,3,4 1,2,3,4
- 拿出4,跟3比,比3大, 1 , 2 , 3 , 4 1, 2, 3, 4 1,2,3,4
总结
0base
-
遍历 [ 1 , n − 1 ] [1,n-1] [1,n−1],表示当前待排序的数
-
遍历到第 i i i 个数时
-
j j j 从 i i i 到 1 1 1,依次比较 a [ j ] a[j] a[j] 和 a [ j − 1 ] a[j - 1] a[j−1]
-
a [ j ] < a [ j − 1 ] a[j] < a[j - 1] a[j]<a[j−1]
s w a p ( a [ j , a [ j − 1 ] ] ) ; swap(a[j, a[j - 1]]); swap(a[j,a[j−1]]);
-
a [ j ] ≥ a [ j − 1 ] a[j] \ge a[j - 1] a[j]≥a[j−1]
break
-
-
代码
0base
#include <stdio.h>
int a[100010], n;
int main() {
scanf("%d", &n);
for (int i = 0; i < n; ++i)
scanf("%d", &a[i]);
for (int i = 1; i <= n - 1; ++i)
for (int j = i; j >= 1; --j)
if (a[j] < a[j - 1]) {
int tmp = a[j];
a[j] = a[j - 1];
a[j - 1] = tmp;
}
for (int i = 0; i < n; ++i)
printf("%d ", a[i]);
return 0;
}
1base
#include <stdio.h>
int a[100010], n;
int main() {
scanf("%d", &n);
for (int i = 1; i <= n; ++i)
scanf("%d", &a[i]);
for (int i = 2; i <= n; ++i)
for (int j = i; j >= 2; --j)
if (a[j] < a[j - 1]) {
int tmp = a[j];
a[j] = a[j - 1];
a[j - 1] = tmp;
}
for (int i = 1; i <= n; ++i)
printf("%d ", a[i]);
return 0;
}
递归
简单算法模板
计数
维护的信息就是数
遍历一遍,满足条件,计数器加一
int cunt = 0;
for (int i = 0; i < n; i++) if () cunt++;
求和
维护的信息就是和
遍历一遍,满足条件,更新sum
int sum = 0;
for (int i = 0; i < n; i++) if () sum += XXX;
求阶乘
维护的信息就是阶乘
int ans = 1;
for (int i = 1; i <= n; i++) ans *= i;
最值
维护的信息就是最值
// 要求最大值,首先初始化为最小值
int maxv = -(1 << 30);
for (int i = 0; i < n; i++) if (a[i] > maxv) maxv = a[i];
维护的信息是最值的下标
int maxi = 0;
for (int i = 1; i < n; i++) if (a[i] > a[maxi]) maxi = i;
平均值
维护的信息就是sum
和cunt
int sum = 0, cunt = 0;
for (int i = 0; i < n; i++) sum += a[i], cunt++;
double avg = 1.0 * sum / cunt;
判断
闰年
if (n % 4 == 0 && n % 100 || n % 400 == 0)
奇数偶数
// 0 ~ n - 1 中的所有偶数
for (int i = 0; i < n; i++)
if (i % 2 == 0) printf("%d ", i);
素数
int n;
// ok标志n是不是素数
int ok = 1;
// n % 3 != 0, n % 4 == 0
// 但凡n % i == 0, 就ok = 0
for (int i = 2; i < n && ok; i++)
if (n % i == 0) ok = 0;