灵神:如何科学刷题? https://leetcode.cn/discuss/post/3141566/ru-he-ke-xue-shua-ti-by-endlesscheng-q3yd/
新动计划: https://leetcode.cn/studyplan/primers-list/

提交之前检查:输入 输出\n return0
运行超时->去检查循环条件

结果 含义 核心排查方向 出错记录
PE 格式错误 空格 / 换行 / 大小写 / 输出顺序 / 字符宽度(如 %5d)/ 标点 打印时多了一个空格
WA 答案错误 逻辑漏洞、边界条件、数据类型溢出、特殊用例未处理(如 0 / 负数 / 极值) 边界0没处理
RE 运行时错误 数组越界、除 0、空指针、栈溢出、递归爆栈、scanf 参数漏 &
TLE 超时 算法复杂度太高、死循环、不必要的重复计算
MLE 内存超限 数组开太大、内存泄漏、重复申请内存
CE 编译错误 语法错误(少分号 / 括号)、头文件缺失、变量未定义、数据类型写错

平衡二叉树要求所有节点的左右子树高度差≤1

C语言零碎点

格式符 含义
%5d 输出整数,占 5 个字符宽度,向右对齐;若数字长度 < 5,左侧补空格;若≥5,按实际长度输出
%-5d 输出整数,占 5 个字符宽度,向左对齐;若数字长度 < 5,右侧补空格(对比用)

在竞赛题中,优先用 double 而非 float ,原因是精度更高、避免计算误差。

快速排序:C 语言标准库提供了函数qsort(nums, numsSize, sizeof(int), cmp)(基于快速排序实现),是算法题中排序的首选。(#include <stdlib.h>
https://www.doubao.com/thread/wc1aabd99c6e3524c

1
2
3
4
//cmp
int cmp(const void*a,const void*b){
return *(int*)a-*(int*)b;
}

一个简单的排序来练手:L1-010 比较大小 - 团体程序设计天梯赛-练习集

四舍五入:(N+1)/2 等价于 N/2 向上取整。

按位异或的运算符在 C 语言中是 ^,运算规则是:对两个数的二进制每一位逐一比较,相同为 0,不同为 1

int在C语言里面的范围是-2^312^31-1
231. 2 的幂 - 力扣

关于字符串

1
2
3
4
    char num[20];
    scanf("%s", num); //直接读取 不需要循环读入单个字符
   
    //scanf("%s")遇到空格/换行就会停止读取

fgets读取含空格字符串的标准方法(会读取‘\0’),参数含义:fgets(数组名, 最大长度, 输入流); 输入流一般是stdin

c语言的字符串以'\0'(ASCII 码 0)结尾。
ASCLL码:

  1. 大写字母 A-Z 的 ASCII 码范围:65-90
  2. 小写字母 a-z 的 ASCII 码范围:97-122
  3. 同一字母的小写比大写大 32(如 A=65,a=97,97-65=32)

#include <string.h>字符串库函数

  • strlen(n)获得该字符串的长度(不包含\0)。
  • strcpy(m, n)把第二个字符串n的内容拷贝到第一个字符串m中。
  • memcpy(m, n, num)将内存地址n处的num 个字节的数据,拷贝到内存地址m处。
  • strcspn(strs, "\n")会扫描字符串 strs,找到第一个出现的 \n(换行符),返回它的下标。如果 strs 里没有 \n(比如读取的字符串长度刚好到 10000),就返回 strs 的总长度(strlen(strs))。

◆关于数组
三种声明方式

  • 数据类型 数组名称[长度n] = {元素1,元素2…元素n};
  • 数据类型 数组名称[长度n];
  • 数据类型 数组名称[] = {元素1,元素2…元素n};
1
2
3
4
5
6
7
8
9
// 输入一个不超过 1000 位的正整数N。

// 可以定义足够大的字符数组存储1000位数字(+1存字符串结束符'\0')
char num[1001];
// 读取字符串形式的数字(而非整数)
scanf("%s", num);

// 后续如果需要字符转数字:'0'的ASCII码是48,num[i]-'0'得到对应数字(0-9)
int digit = num[i] - '0';

memset(cnt, 0, sizeof(cnt)):批量初始化 cnt 数组为 0,第三个参数表示总字节数。
1365. 有多少小于当前数字的数字 - 力扣(LeetCode)

◆关于指针
int* arrint *arr 是等价写法。

1
2
3
4
5
6
7
int arr[5] = {1,2,3,4,5}; 
//定义指针p并初始化,把arr的首地址赋值给p
int *p = arr;

// 所以,*p是值,p是地址。
// *p = *(p + 0) = p[0]
// *(p + i) = p[i]

返回指针类型的函数,return p而不是return *p,如果是后者,那么函数返回类型应该是int

int *P=(int*)malloc(sizeof(int)*1)为指针分配空间的函数。
free(p);//回收
int *p = (int*)calloc(4, sizeof(int)) 为指针分配空间的函数并初始化为0。(<stdlib.h>)

realloc(原内存指针, 新的内存大小) 是 C 语言标准库(<stdlib.h>)中的函数,全称re-allocate,作用是调整已分配堆内存的大小,让内存 “按需分配”,避免浪费。

1
2
3
4
5
// 第一步:预分配“足够大”的内存(比如这里先分配最多的2*n个空间) 
char **ans = malloc(sizeof(char*) * 2 * n);
// ... 填充操作,最终实际用到p个位置 ...
// 第二步:缩容内存到实际需要的大小
ans = realloc(ans, sizeof(char*) * p);

其他函数
llabs() 函数:全称 long long absolute value,作用是返回 long long 类型的绝对值(对应 int 类型的 abs())。

数学

闰年的判别条件是:该年年份能被4整除但不能被100整除,或者能被400整除。闰年的2月有29天。

素数(质数)的定义:大于 1 的自然数,除了 1 和它本身,不能被其他自然数整除

向上取整

  • 12/5 = 2.4 → 向上取整 = 3;
  • 10/5 = 2.0 → 向上取整 = 2;
  • 7/3 = 2.333 → 向上取整 = 3;
  • 0/5 = 0 → 向上取整 = 0。
    (a + b - 1) / b 实现向上取整。
取整需求 公式(整数除法) 例题
向上取整 (a + b - 1) / b 码蹄集OJ-小码哥的魔法实战1
向下取整 a / b
四舍五入 (a + b/2) / b

圆周长:C=2πr;面积:S=πr2;直径:d=2r
球体表面积:S=4*π*r^2;体积:V=3/4​*π*r^3

辗转相除法的数学原理
核心定理:gcd(a, b) = gcd(b, a % b)a ≥ b > 0
即「两个数的最大公约数」等于「第二个数」和「第一个数除以第二个数的余数」的最大公约数。
当余数为 0 时,此时的 a 就是最大公约数。

1
2
3
4
5
6
7
8
9
10
// 辗转相除法求最大公因数(GCD) 
long long gcd(long long a, long long b) {
a = llabs(a), b = llabs(b);
while (b) {
long long t = a%b;
a = b;
b = t;
}
return a;
}

263. 丑数 - 力扣(LeetCode)

二进制与位运算

两大类:逻辑位运算符、位移运算符

位与& 二进制 同1异0
位或| 都为0才是0 其他全为1
异或^ 同0异1
按位取反 ~ 转化为二进制再取反

左移x<<y x转化为二进制,左移y位,末尾补0
右移x>>y x非负数,高位补0;x负数,高位补1。

C 语言中运算符的优先级: ==(相等判断) > &(按位与) > &&(逻辑与)

231. 2 的幂 - 力扣(LeetCode)

简单题

最大公约数和最小公倍数

1
2
3
4
5
6
7
8
9
10
11
12
13
#include "stdio.h"
void main(){
int m,n,zdgys,zxgbs,i;
scanf("%d%d",&m,&n);//m<n
//计算最大公约数
for(i=m;i>=1;i--){ //它们的最大公约数就是较小数
if(m%i==0 && n%i==0){
zdgys=i;
break;
}
}
zxgbs=m*n/zdgys;
}

计算天数

题目:在一行输出日期是该年中的第几天。
输入:2009/03/02
输出:61

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
 #include "stdio.h"
int main() {
int year,month,day;
int dayinmonth[]={31,28,31,30,31,30,31,31,30,31,30,31};
int totaldays=0;
int i=0;
scanf("%d/%d/%d",&year,&month,&day);//读入

if((year%4==0&&year%100!=0)||(year%400==0)){
dayinmonth[1]=29;
for(int i=0;i<month-1;i++){
totaldays=totaldays+dayinmonth[i];
}
totaldays+=day;
printf("%d\n",totaldays);
}else{
for(int i=0;i<month-1;i++){
totaldays=totaldays+dayinmonth[i];
}
totaldays+=day;
printf("%d\n",totaldays);
}
return 0;
}

输出整数各位数字

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#include "stdio.h"
int main(){
long N;
int t,i=0,y=0;
int a[99]; //使用新的数组来储存
scanf("%ld",&N); //%ld 长整型
t=N;
if(N==0) printf("0 "); //注意讨论零
else{
while (N!=0) {
a[i]=N%10;
N/=10;
i++; //循环更新得末位,i++
}
for(y=i-1;y>=0;y--){ //数组倒序输出
printf("%d ",a[y]);
}}return 0;
}

同类题目:将整型数x中每一位能被3整除的数依次取出。例如,当x中的数为:97653140时,px中的数为:9630;如果没有满足要求的数则输出x。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
void fun(int x, int *px) {
int new_num=0,digit,a=1; //a初始化为1
int t=x;
while(t>0){
digit=t%10;
t/= 10;
if(digit%3==0){
new_num+=digit*a;
a=a*10; //每执行一次,a改变 巧!!!
}
}
if(new_num==0){
*px = x;
} else {
*px=new_num;
}
}

同类题目:求一批整数中出现最多的个位数字
输入:3
1234 2345 3456
输出:3: 3 4

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
#include "stdio.h"
int main() {
int n,i,M;
scanf("%d", &n);
int arr[n];
int count[10];
for(i=0;i<10;i++){
count[i]=0; //初始化 1到9出现的次数全为0
}
for(i=0;i<n;i++){
scanf("%d",&arr[i]);
}
for(i=0;i<n;i++){
if(arr[i]==0)count[0]++;
while(arr[i]!=0){
count[arr[i]%10]++;
arr[i]/=10;
}}
M=0;
// 寻找count[]最大值
for(i=0;i<10;i++){
if(count[i]>M){
M=count[i];
} }
printf("%d:",M);
// 打印出现最多次的数值
for (i=0;i<10;i++){
if(count[i]==M){
printf(" %d", i);
}
}
printf("\n");
return 0;
}

同类题目:输出一个整数的逆序数

1
2
3
4
5
6
7
8
9
int reverse(int number) {
int a=0;
while(number!=0) {
int b=number%10; // 获取当前最低位的数字
a=a*10+b; // ***逆序排列***
number=number/10; // 去除最后一位数
}
return a;
}

判断是否是回文数:
如果输入是超大数(如1000000000000000000),int类型会溢出(int 最大约 21 亿)。
所以用指针字符串传参更好。

1
2
3
4
5
6
7
8
9
10
11
int is_huiwen(char *nums){
    int len = strlen(nums);
    int flag = 1;
    for(int i=0;i<len/2;i++){
        if(*(nums + i) != *(nums + len -1 -i)){
            flag = 0;
            break;
        }
    }
    return flag;
}

字符串数字-1:

1
2
3
4
5
6
7
8
9
10
11
12
char* str_decrease(char *p){
    int len = strlen(p);
    int i = len -1;
    while(i>=0 && p[i]=='0'){
        p[i] = '9';
        i--;
    }
    if(i>=0){
        p[i]--;
    }
    return p;
}

二叉树最大深度

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     struct TreeNode *left;
 *     struct TreeNode *right;
 * };
 */
int maxDepth(struct TreeNode* root) {
    if(root == NULL){
        return 0;
        // return 0并不是 “整个递归结束”,而是当前这一层递归函数的结束,会把0返回给 “调用它的上一层”,然后上一层继续执行后续逻辑。
    }
    // 递归 结果自下而上
    int l_depth = maxDepth(root -> left);
    int r_depth = maxDepth(root -> right);
    return MAX(l_depth, r_depth) + 1;
}

自上向下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     struct TreeNode *left;
 *     struct TreeNode *right;
 * };
 */

void DFS(struct TreeNode* root, int depth, int *ans) {
    if(root == NULL){
        return;
    }
    depth++;
    *ans = MAX(*ans, depth);
    DFS(root -> left, depth, ans);
    DFS(root -> right, depth, ans);
}

int maxDepth(struct TreeNode* root) {
    int ans = 0;
    DFS(root, 0, &ans);
    return ans;
}

类似题目:判断两棵树是否相同

  • 终止条件:如果pq有一个是空节点 时, 只有当pq都为空时才返回true,否则返回false。
  • 递归条件:如果pq都非空 → 必须满足 3 个条件才返回true。
    • 当前节点值相等(p->val == q->val
    • 左子树完全相同(isSameTree(p->left, q->left)
    • 右子树完全相同(isSameTree(p->right, q->right)
  • 只要有一个条件不满足,整体返回false。
1
2
3
4
5
6
bool isSameTree(struct TreeNode* p, struct TreeNode* q) {
    if(p == NULL || q == NULL){
        return p == q; //只有当p和q都为空时才返回true,否则返回false
    }
    return p->val == q->val && isSameTree(p->left,q->left) && isSameTree(p->right, q->right);
}

类似题目:判断是否是对称树

1
2
3
4
5
6
7
8
9
10
bool isSameTree(struct TreeNode* left, struct TreeNode* right){
if(left == NULL || right == NULL){
return left == right;
}
return left->val == right->val && isSameTree(left->left, right->right) && isSameTree(left->right, right->left);
}

bool isSymmetric(struct TreeNode* root) {
return isSameTree(root->left, root->right);
}

类似题目:平衡二叉树 110. 平衡二叉树 - 力扣(LeetCode)

排序

561. 数组拆分 - 力扣(LeetCode)
快速排序+偶数下标求和:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
int cmp(const void* a, const void* b){
int x = *((int*)a);
int y = *((int*)b);
return x - y;
}

int arrayPairSum(int* nums, int numsSize) {
qsort(nums, numsSize, sizeof(int), cmp);
int sum =0;
for(int i=0;i<numsSize;i++){
if(i % 2 == 0){
sum += nums[i];
}
}
return sum;
}