目录
一、科软机试注意事项
- 在待完成的注释处写代码完成函数,main函数中的printf用于测试
- 函数中不要加注释,否则会随机报错,你找半天都找不出哪里出错
- 好好检查for循环和递归,死循环死递归直接卡2分钟把你心态搞崩
- oi赛制,按照测试用例给分,一道题5个测试用例,一个用例5分,考试时不会告诉你提交的代码通过几个用例,只能自己调试
- 只要时间复杂度不是太过分(指数级别),则一般能拿全分
- 能不能加头文件,不好说,有人说能有人说不能,个人觉得只要在线编译能通过就能用
二、leetcode注意事项
1. returnSize和returnColumnSize
- 如果返回值是一个一维数组的话,必定出现returnSize,它是一个int类型的指针,其指向的地址单元保存了数组中元素个数,
*returnSize = 2
表示返回值数组中元素的个数为2 - 如果返回值是一个二维数组的话,就必定同时出现returnSize和returnColumnSizes,returnSize是一个一级指针,其指向的地址保存的内容是二维数组的行数,returnColumnSizes是一个一维指针数组,保存返回值中各行的一维数组中元素个数
- returnColumnSize不能用memset赋值,而且用for循环赋值时(*returnColumnSizes)必须加括号
*returnSize = 3; //二维数组行数为3
*returnColumnSizes = (int*)malloc(3*sizeof(int)); //需要3个单元来记录各行元素个数
(*returnColumnSizes)[0] = 2; //二维数组第0行有2个元素
(*returnColumnSizes)[1] = 3; //第1行有3个元素
(*returnColumnSizes)[2] = 4; //第2行有4个元素
2. 其他注意事项
- 一定要注意输入元素为NULL等情况,我经常在这里犯错
- 链表最后一个节点的next指针置为NULL,否则会报错
- 在循环中定义数组(静态),编译器不会重复创建这个数组,因此不用担心内存爆炸的问题
- leetcode中返回的数组必须使用动态分配内存的数组,也就是必须要malloc
- 在开始编码前要考虑到各种极端情况(如爆int),所以我默认都定义long long类型就不容易出错哈哈哈,或者习惯上把减法放在加法前面
- 整数最大值为INT_MAX = 0x7FFFFFFF,最小值为INT_MIN = 0x80000000,学过计算机组成原理应该容易理解
- 字符串一定要记得有’\0‘,反斜杠0
- 全局变量必须在main函数中初始化,不要在外面初始化
- 数组的拷贝:int* arr1 和arr2,拷贝arr2到arr1不是直接给arr1赋值(浅拷贝),而是将arr2的元素挨个赋值给arr1,或者用memcpy(arr1,arr2,sizeof(arr2)/sizeof(int))的方式
- printf(“%d”,fmin(INT_MIN, INT_MAX));打印结果为0,因为fmim返回double类型
三、C语言小技巧
1. 数组初始化
将数组元素都置0
#include<string.h>
/*
注意别踩坑:
1.sizeof里面的数组只能是静态数组!!!
2.如果是用malloc分配内存的数组,malloc参数怎么写这里第三个参数就怎么写
3.稍不注意,直接buffer overflow
*/
int buf[255];
memset(buf, 0, sizeof(buf));//静态数组初始化
int* buf2 = (int*)malloc(255*sizeof(int));
memset(buf2,0,255*sizeof(int));//动态数组初始化
坑2: memset是按照一个字节一个字节赋值的,赋值char类型的数据永远不会出错,但是如果赋值超过1个字节的数据则有可能出现读取错误,因此初始化非char类型数组时,只能赋值为0或-1
2. 功能强大的sprintf函数和sscanf函数
#include<stdio.h>
char str[200];
//sprintf和printf效果一样,只不过一个输出到控制台,一个输出到数组中
//结合两函数可以轻松实现进制转换与字符串与数字之间转换
//注意C语言没有二进制的格式化方法,转为2进制需要自己写了
sprintf(str,"%d",12345); //12345转为10进制字符串
sprintf(str,"%x",1234); //转16进制
sprintf(str,"%o",1234); //转8进制
sprintf(str,"%d",0b10000000); //2进制转10进制
//sscanf函数和scanf效果一样,只不过一个是键盘读入数据一个是从字符串读入数据
int dest;
char* str= "123";
//sprintf是将第三个参数转换后送给第一个参数,sscanf参数是将第一个参数转换送送给第三个参数
//和scanf一样要加&,不然报错!!!
sscanf(str,"%d",&dest);
//二者区别就像printf和scanf的区别
感谢好友的补充:
- 对于超出整型范围的字符串的字符串,sscanf会将该字符串转换为该整型类型的最大值或最小值
3. 快速排序库函数
- 快速排序时间复杂度为 O ( n l o g 2 n ) O(nlog_2 n) O(nlog2n)
- cmp比较函数需要自己写
#include<stdlib.h>
//cmp比较函数需要自己写!
/*
形参为void*的意思是能接收所有类型的数据
返回值将void*强制转换为(int*)然后*(int*)是指针指向的值
a - b是升序排序,b - a是降序排序
*/
/**
*写成这样唯一不好的一点是:如果a-b的值超出int范围了
*排序就会失败(改long long没用,qsort只接受返回值为
*int类型的函数)
*/
int cmp(const void* a, const void* b){
return *((int*)a) - *((int*)b);
}
//这样写就可以避免溢出问题,是一个完美的排序函数,降序只需要将大于号改成小于号
int cmp(const void* a,const void* b){
return !(*(int*)a==*(int*)b) && *(int*)a > *(int*)b ? 1 : -1;
}
int arr[] = {-5,-3,6,0,0,-4,6,7,-9};
qsort(arr, len, sizeof(int), cmp);
- 扩展:成绩按从低到高排序,如果成绩相同则按学号大小排序
- 提交网址:成绩排序(清华大学复试上机题)
#include <stdio.h>
#include<stdlib.h>
struct stu {
int id;
int score;
};
int cmp(struct stu* s1, struct stu* s2){
if((*s1).score==(*s2).score){
return (*s1).id-(*s2).id;
}
return (*s1).score - (*s2).score;
}
int main() {
int n;
struct stu* s;
while (scanf("%d", &n) != EOF) {
s = (struct stu*)malloc(n * sizeof(struct stu));
int i, j;
for (i = 0; i < n; i++)
scanf("%d %d", &s[i].id, &s[i].score);
qsort(s,n,sizeof(struct stu),cmp);
for(i=0;i<n;i++){
printf("%d %d\n",s[i].id,s[i].score);
}
}
return 0;
}
4. 二分查找库函数
#include<stdio.h>
#include<stdlib.h> //bsearch函数是stdlib头文件中的函数
//和qsort的cmp一模一样
int cmp(const void* a, const void* b){
return *(int*)a - *(int*)b;
}
/*
参数一:目标值的地址
参数二:数组首地址
参数三:要查找的长度
参数四:函数指针cmp
返回值:目标值的地址,用该地址减去数组首地址就是目标值的下标,若目标值不存在则返回NULL
注意:数组必须有序,刷力扣时尽量少用库函数,避免知其然而不知其所以然
*/
int main() {
int nums[] = {123, 145, 512, 627, 800, 933,0};
int key = 512;
int len = 6;
int* index = bsearch(&key,nums,len,sizeof(int),cmp);
if(index)
printf("%d\n",index-nums);
return 0;
}
//自己写也很简单,不要太迷恋库函数
int search(int* nums, int numsSize, int target) {
int left = 0, right = numsSize - 1;
while(left<=right){
int mid = (right-left)/2 + left; //避免爆int
if(nums[mid]==target)
return mid;
else if(nums[mid]>target)
right = mid - 1;
else
left = mid + 1;
}
return -1;
}
5.哈希表
在leetcode中经常需要用到用到键值对索引的情况,但是C语言中并没有哈希表的数据结构,但是可以用数组下标来模拟
int hashTable[130];
memset(hashTable, 0, sizeof(hashTable); //注意#include<string.h>
hashTable['A'] = 1; //就像<'A', 1>键值对一样
6.栈
leetcode中dfs、括号匹配、中缀表达式转后缀表达式问题中经常需要使用到栈,但是C语言中没有栈这个数据结构,但是我们可以用数组来模拟
int stack[10005]; //容量尽可能定义大一点
int top = 0; //栈顶元素
stack[top++] = 2; //将2压入栈
int tmp = stack[--top];//弹栈
7.队列
leetcode中bfs等题目中需要用到队列,C语言中也没有队列这个数据结构,和栈一样,我们仍然可以用数组来模拟
int queue[10005];
int front = 0, rear = 0;
queue[rear++] = 2; //入队
int tmp = queue[front++];//出队
8.其他
8.1 判断字符是否是数字
#include<ctype.h>
int isdigit(int c); //如果c是数字,则返回非0整数,否则返回0
//记不住直接这样写
if(ch>='0'&&ch<='9')
8.2 整型最大值
注意:int范围和long范围一样,在机试中一定要用long long类型
#include<limits.h>
#include<stdio.h>
int M1 = INT_MAX;
int m1 = INT_MIN; //其他类型同理
long long mm1 = LONG_LONG_MAX;
//也可以这么写,注意有的编译器可能会报错,报错的话把后面的H去掉
int M2 = 0x7fffffffH;
int m2 = 0x80000000H;
int main(){
printf("%d\n",M1);
printf("%d\n",m1);
printf("%ld\n",LONG_MAX);
printf("%lld\n",mm1);
return 0;
}
四、力扣常见报错
错误提示1:
ERROR: AddressSanitizer: heap-buffer-overflow on address 0x6160000002d8 at pc 0x556c2692357e bp 0x7ffca71fc0d0 sp 0x7ffca71fc0c0
调试:科软机试只能用printf查看自己程序哪里错了,所以要多写printf,可以很快找到自己的程序哪里出错了
错误原因:使用了未分配的空间,数组越界。
- 数组定义的空间不够大,导致越界,我还使用了sizeof(arr)导致错上加错,要注意sizeof()只能求静态数组的空间大小,malloc分配内存的数组sizeof()统一会返回8。
- memset函数报错,
memset(arr,0 ,sizeof(arr);
,如果arr是静态数组就可以这么初始化,如果这个arr是malloc动态分配内存的数组,sizeof默认返回8,所以就报数组越界错了,第三个参数最好是前面malloc里面怎么写这个就怎么写 int* ans = (int*)malloc(len1*sizeof(char));
sizeof里面int误写成char
错误提示2:
Line 25: Char 39: runtime error: store to address 0x62e00059e040 with insufficient space for an object of type 'struct TreeNode *' [solution.c]
错误原因:给数组分配的内存空间不够
(struct TreeNode**)malloc(10000*sizeof(int));
把sizeof(struct TreeNode*)写成了sizeof(int),导致分配的内存空间不够
错误提示3:
Line 26: Char 17: runtime error: shift exponent -1 is negative [solution.c]
错误原因:位运算移位的位数只能是非负整数
- 2<<0 - 1,由于位运算优先级非常低,该表达式会优先进行减法运算,因此导致移位负数,因此需要给位运算加括号,改成 (2<<0) - 1
错误提示4:
Line 96: Char 17: runtime error: member access within misaligned address 0xbebebebebebebebe for type 'struct TreeNode', which requires 8 byte alignment [TreeNode.c] 0xbebebebebebebebe: note: pointer points here <memory cannot be printed>
错误原因: 二叉树节点左子树与右子树没有初始化为NULL,或链表末尾没有指向NULL
错误提示5:
AddressSanitizer:DEADLYSIGNAL ==21==ERROR: AddressSanitizer: SEGV on unknown address 0x000000000000 (pc 0x7ff25e341149 bp 0x7ffd63421960 sp 0x7ffd63421260 T0)
错误原因: scanf或者sscanf没加&
错误提示6
测试样例单独测试都能通过,但是放到一起测试却显示报错
错误原因: 代码中有全局变量没有初始化,或者没有写*returnSize = 0;
五、完结撒花
初试384分排415名,复试81.93分(184.3分)排102名,总分排235,逆袭了180名,明天就写经验贴