前言
为准备大四华为笔试,决定使用学长推荐的《算法笔记》一书来学习,配套有《算法笔记·上机训练实战指南》,希望学习完此书,刷完力扣,能够通过笔试,为拿到大厂offer奠定基础💪
此文为笔记系列第五篇。
我的笔记
1.数组就是相同数据类型的变量组合在一起的集合,每个变量都有其存放地址,数组就是从某个地址开始连续若干个位置形成的元素组合;数组的大小必须为整数常量,不可为变量;数组长度为size,只能访问下标为0~size-1的元素;
2.数组初始化,使用{a[0],a[1],a[2],a[3],...,a[n-1]}来给数组a[n]赋初值,可以只给部分数据赋初值,未被赋初值的元素一般默认初值为0;
①未赋初值,数组元素数值随机
②部分元素赋初值,未被赋初值元素默认为0
③a[n]={0}或a[n]={},数组元素均为0
3.递推是指不断让后一位的结果由前一位或几位计算得到;递推可以分为顺推和逆推;
4.在练习书中关于递推的示例时,发现自己一个认知方面的问题
该示例是使用顺推,使后一个元素等于前一个元素的两倍,我一开始的代码如下:
#include <stdio.h>
int main() {
int a[10];
a[0] = 1; // 给数组中某一个元素赋值,不用大括号,将数组某一元素当作一个变量就好
for(int i = 1; i<=9; i++) {
printf("a[%d] = %d\n", i, 2*a[i-1]);
}
return 0;
}
编译运行后发现除a[1]数值正确外,别的都是随机值,后仔细思索,发现我并未将得到的a[1]存储在a[1]中,只是将a[0]的数值乘以2输出出来,所以调用得a[1]的值还是编译器给的随机初值,循环无法顺推下去!这就是我认知上的问题了,归根结底还是没有了解计算机的特性,还需进一步学习了解代码运行的深层逻辑。
回到该问题,添加一行代码即可:
#include <stdio.h>
int main() {
int a[10];
a[0] = 1;
for(int i = 1; i<=9; i++) {
a[i] = 2 * a[i - 1]; //将结果保存到对应的元素中
printf("a[%d] = %d\n", i, a[i]);
}
return 0;
}
另外为满足书中示例的手动输入a[0],将代码进行修改:
#include <stdio.h>
int main() {
int a[10];
// a[0] = 1;
// a[0] = getchar();
// 此处注意,getchar()为输入单个字符,输入为1时,会识别为ASCII码,1对应的ASCII码为49,
// 所以后续调用a[0]的数值为49
scanf("%d", &a[0]);
printf("a[0] = %d\n", a[0]);
for(int i = 1; i<=9; i++) {
a[i] = 2 * a[i - 1];
printf("a[%d] = %d\n", i, a[i]);
}
return 0;
}
5. 在练习书中冒泡排序的代码时,我深刻认识到之前书中一句话,if语句中的大括号不要轻易去掉😅我是笨蛋
另外,我本想自己根据冒泡排序的思想写出对应代码,但最终写出了选择排序的代码,还是对冒泡排序理解不到位。
#include <stdio.h>
int change(int *x, int *y) {
int t;
t = *x;
*x = *y;
*y = t;
}
//此处本想尝试函数调用,结果始终调用不了,
//搜索看到选择排序代码,发现使用了指针,暂时不懂,学完回来再看。
int main() {
int a[5] = {3, 1, 4, 5, 2};
int t, i, j;
for(i = 0; i < 4; i++) {
for(j = i + 1; j <= 4; j++) {
if(a[i]>a[j]) {
change(&a[i],&a[j]);
// t = a[i];
// a[i] = a[j];
// a[j] = t; //change函数完全可以用这三行代码代替
}
}
}
for(i = 0; i <= 4; i++) {
printf("a[%d] = %d\n", i, a[i]);
}
return 0;
}
重新学习冒泡排序思想:本质为交换。
它重复地走访过要排序的数列,一次比较两个元素,如果他们的顺序错误就把他们交换过来。走访数列的工作是重复地进行直到没有再需要交换,也就是说该数列已经排序完成。这个算法的名字由来是因为越小的元素会经由交换慢慢"浮"到数列的顶端。(来源于菜鸟教程)
我的思考过程:先写出走一趟的程序,然后再外套走多趟的程序
遇到的问题:怎样确认已经排好序?
解决方案:通过思考走一趟怎样停止,结合冒泡排序具体过程,即可明确。
#include <stdio.h>
int main() {
int a[5] = {3, 1, 4, 5, 2};
int i, t, j;
int len = 5; //len代指数组长度
for(i = 0; i < len; i++) { //i代表趟数,一共需要走(len-1)趟
for(j = 0; j + 1 < len - i; j++) { //走一趟,就有一个数固定,不需要再被比较交换
if(a[j]>a[j+1]) {
t = a[j];
a[j] = a[j+1];
a[j+1] = t; //此处可以换为上个代码中的change函数
}
}
}
for(i = 0; i <= 4; i++) {
printf("a[%d] = %d\n", i, a[i]);
}
return 0;
}
6.二维数组a[m][n]可以看作m个长度为n的一维数组,初始化与一维数组类似,例如:
a[3][4] ={ {1,2,3,4},{5,6,7,8},{9,10,11,12} } 输出后为:
a[3][4] ={ {1,2,3},{},{9,10} } 输出后为:
二维数组输入输出需要挨个输入输出。
7.memset函数 对数组中的每一个元素赋相同的值
memset(数组名,值,sizeof(数组名)) //开头加string.h头文件
//建议只给数组赋值为0或-1;赋其他值,使用fill函数
//memset中第三个注意不可以直接写数组a的长度,如5,否则数组值不全为0
#include <stdio.h>
#include <string.h>
int main() {
int a[5];
memset(a, 0, sizeof(a));
for(int i = 0; i<5; i++) {
printf("%d ", a[i]);
}
printf("\n");
memset(a, -1, sizeof(a));
for(int i = 0; i<5; i++) {
printf("%d ", a[i]);
}
return 0;
}
8.字符数组初始化:可以和普通数组一样赋值单个字符;也可以直接赋值整个字符串(这种操作仅限于初始化操作);注意初始化操作是指在定义变量时便给变量赋值,定义后的赋值不算是初始化操作!
字符数组输入输出:
①scanf printf %c 输入单个字符,可输入空格;%s输入字符串并赋值给字符数组,以空格和换行来判定输入结束;另外,scanf("%s", str) 其中,str前无需取地址符&;
②getchar()、putchar(): 只用来输入输出单个字符;注意输入输出时换行符的处理
③gets、puts:gets输入一行字符串(以换行符判断结束),将其存放于一维数组或二维数组的一维中; puts输出一行字符串(紧跟一个换行符)
char str1[20], str2[5][10];
gets(str1);
gets(str2[2]);
puts(str1);
puts(str2[2]);
字符串存储方式:
在一维数组或者二维数组第二维的末尾会有一个空字符\0,表示存放字符串的末尾;
①③两种输入方式会自动填补空字符,②getchar()输入需要在输入的每个字符串后面加如“\0”;
\0结束符ASCII码为0,占用一个字符位;空格ASCII 码为32;
只有char字符数组末尾需要加空字符,因此字符数组的长度一定要比实际存储字符串长度至少多1。
9.用于字符数组的函数: <string.h>
用于字符数组的函数 | 作用 |
---|---|
strlen(字符数组) | 得到字符数组中第一个\0前面字符的个数 |
strcmp(字符数组1, 字符数组2) | 返回两字符数组大小的比较结果(按字典序) |
strcpy(字符数组1, 字符数组2) | 把字符数组2复制给字符数组1,包括\0 |
strcat(字符数组1, 字符数组2) | 把字符数组2接到字符数组1后面 |
10.sscanf和sprintf
sscanf = string + scanf sprintf = string + printf
sscanf(str, "%d", &n); // 将字符串str从左到右输入到n中
sprintf(str, "%d", n); // 将n从右到左写到字符串str中
sscanf 和 sprintf 使用和 scanf 和 printf 类似。