目录
注:
本笔记参考B站up鹏哥C语言的视频
添加
- 创建的变量可以通过free释放内存 - 动态内存开辟
申请 - malloc
使用
释放 - free(变量不会被主动置为空指针,需要手动修改,如:
int* p = malloc(40); //使用p指向的40个字节的空间 free(P);
注:free命令执行结束后,p依旧指向本来的地址。free命令不会把这个指针置为NULL。需要加上:
p = NULL;
- 在定义函数指针时,对应函数的函数名可省略。
例1:杨氏矩阵
题目内容:有一个数字矩阵,矩阵的每行从左到右是递增的,矩阵从上到下是递增的,请编写程序在这样的矩阵中查找某个数字是否存在。
要求:时间复杂度小于O(N)。
ps:① O(N) - 查找一个元素的次数和数字元素的个数(N)是有关系的。
② O(1) - 此时查找次数是3、5、7……等等与N无关的常数。
一个标准的杨氏矩阵,如:
1 2 3 4
2 3 4 5
4 5 6 7
不符合要求的算法(遍历数组的方法):
N - 查找的次数最坏的情况下是N次(乃至2N、3N……)
int main()
{
int arr[3][3] = { 1, 2, 3, 4, 5, 6, 7, 8, 9 };
//查找一个数字,比如:7
int i = 0;
int j = 0;
for ( i = 0; i < 3; i++)
{
for (j = 0; j < 3; j++)\
{
if (arr[i][j] = 7)
{}
}
}
return 0;
}
符合要求的算法(这种查找的方式可以更快的找到目标):
如:利用数组右上角的数字(或者左下角的数字)进行比较。
- 从第一行的最大值开始查找,比较3与7的大小,发现3<7,所以可以肯定7不在第一行。去掉第一行,向下查找。
- 同样的方法可以去掉第二行。
- 在第三行发现9>7,开始在这一行查找,并且可以去掉9所在的这一列。
- 发现8>7,去掉8所在的这一列。
- 找到7。
举例:寻找7
int find_num(int arr[3][3], int r, int c, int k)
{
int x = 0;
int y = c - 1;
while (x < r && y >= 0)
{
if (arr[x][y] < k)
{
x++;//去掉行
}
else if (arr[x][y] > k)
{
y--;//去掉列
}
else
{
return 1;//找到了
}
}
return 0;//找不到
}
int main()
{
int arr[3][3] = { 1, 2, 3, 4, 5, 6, 7, 8, 9 };
int k = 7;//目标
//规定:如果找到,返回1;找不到,返回0。
int ret = find_num(arr, 3, 3, k);
if (ret == 1)
printf("找到了\n");
else
printf("找不到\n");
return 0;
}
注:上述代码难以和后续功能合并,以下提供优化的代码(返回型参数的运用)。
int find_num(int arr[3][3], int* px, int* py, int k) { int x = 0; int y = *py - 1; while (x < *px && y >= 0) { if (arr[x][y] < k) { x++; } else if (arr[x][y] > k) { y--; } else { *px = x; *py = y; return 1; } } return 0; } int main() { int arr[3][3] = { 1, 2, 3, 4, 5, 6, 7, 8, 9 }; int k = 7;//目标 int x = 3;//行 int y = 3;//列 //如果找到,返回1;找不到,返回0。 int ret = find_num(arr, &x, &y, k); if (ret == 1) { printf("找到了\n"); printf("下标是%d %d\n", x, y); } else { printf("找不到\n"); } return 0; }
实际上是把原本传过去的行(x)和列(y)改为了行的地址(*px)和列的地址(*py)。
两个指针在这里面的两个主要作用:
- 传入参数
- 带回值
注意:形参是实参的临时拷贝。
例2:字符串左旋(来源:CSDN博主 v_JULY_v)
题目内容:首先一个函数,可以左旋字符串中的k个字符。
例如:
ABCD左旋一个字符得到BCDA
ABCD左旋两个字符得到CDAB
注意:
char* p = "ABCDEF";//这种写法是错误的,"ABCDEF"是常量字符串,地址无法改变
解法一
思路:
int main() { char arr[10] = "ABCDEF";//目标字符串 int k = 2;//旋转次数 str_le_rot(arr, k);//实现左旋 printf("%s\n", arr);//打印 return 0; }
A B C D E F 1.创建一个新的空间,拿取首字符放入;
B C D E F
A 2.剩余字符串向左移动一个单位;
B C D E F 3.将首字符放入末尾位置;
B C D E F A 4.再拿取首字符,循环步骤……
代码:
#include<stdio.h>
void str_le_rot(char* str, int k)
{
int i = 0;
int len = strlen(str);
for (i = 0; i < k; i++)
{
//每次左旋一个字符
char tmp = *str;
//后面的n - 1个字符往前移动
int j = 0;
for (j = 0; j < len - 1; j++)
{
*(str + j) = *(str + j + 1);
}
//tmp内存储的字符放在最后
*(str + len - 1) = tmp;
}
}
int main()
{
char arr[10] = "ABCDEF";
int k = 2;
str_le_rot(arr, k);
printf("%s\n", arr);
return 0;
}
解法二(三步反转法)
思路:
int main() { char arr[10] = "ABCDEF"; int k = 10; str_le_rot(arr, k); printf("%s\n", arr); return 0; }
1.将字符串分为两部分
A B C D E F 2.将两部分分别逆序
B A F E D C 3.整体进行逆序
C D E F A B
代码:
#include<stdio.h>
#include<assert.h>
void reverse(char* left, char* right)
{
assert(left);
assert(right);
while (left < right)
{
char tmp = *left;
*left = *right;
*right = tmp;
left++;
right--;
}
}
void str_le_rot(char* str, int k)
{
assert(str);
int len = strlen(str);
reverse(str, str + k - 1);//逆序左边
reverse(str + k, str + len - 1);//逆序右边
reverse(str, str + len - 1);//逆序整体
}
int main()
{
char arr[10] = "ABCDEF";
int k = 2;
str_le_rot(arr, k);
printf("%s\n", arr);
return 0;
}
注:这串代码当k>数组长度时,会由于 str + k 的相关元素越界而出现问题。
例3:字符串旋转结果
题目内容:写一个函数,判断一个字符串是否为另外一个字符串旋转之后的字符串。
例如:
给定 s1 = AABCD 和 s2 = BCDAA ,返回1;
给定 s1 = abcd 和 s2 = ACBD ,返回0。
解法一(穷举)
思路:
int main() { char arr_1[] = "AABCD"; char arr_2[] = "BCDAA"; return 0; }
通过不断左旋字符串arr_1(旋转一个字符,旋转两个字符……),于字符串arr_2进行比较,得出结论。
代码:
#include<stdio.h>
#include<string.h>
is_str_le_rot(char* str1, char* str2)
{
int i = 0;
int len = strlen(str1);
for (i = 0; i < len; i++)
{
//每次左旋一个字符
char tmp = *str1;
//后面的n - 1个字符往前移动
int j = 0;
for (j = 0; j < len - 1; j++)
{
*(str1 + j) = *(str1 + j + 1);
}
//tmp内存储的字符放在最后
*(str1 + len - 1) = tmp;
//比较str1和str2
if (strcmp(str1, str2) == 0)
{
return 1;
}
}
return 0;
}
int main()
{
char arr_1[] = "AABCD";
char arr_2[] = "BCDA";
int ret = is_str_le_rot(arr_1, arr_2);
if (ret == 1)
{
printf("yes\n");
}
else
{
printf("no\n");
}
return 0;
}
解法二(使用库函数)
思路:
对于字符串1"AABCD",
可以创建对应字符串2"AABCDAABCD",通过字符串2就可以找到所有字符串1的旋转结果。
知识点:
strcat函数 - 在字符串后面添加一个字符串
使用例:
#include<stdio.h> #include<string.h> int main() { char arr[20] = "Hello"; strcat(arr, " World"); printf("%s\n", arr); return 0; }
打印结果:
注意:
strcat(str1, str1);
这种写法是不成立的。需要使用 strncat函数 。
对比strcat函数,strncat函数多了一个参数。
使用例:
#include<stdio.h> #include<string.h> int main() { char arr[20] = "Hello"; strncat(arr, arr, 5); printf("%s\n", arr); return 0; }
打印结果:
strstr函数 - 用来检测一个字符串是不是另一个字符串的子串
这个函数,如果发现一个字符串是另一个字符串的子串,就会返回这个子串所在的位置(返回地址);如果找不到,就会返回一个空指针(NULL)。
例子:
代码:
#include<stdio.h>
#include<string.h>
is_str_le_rot(char* str1, char* str2)
{
//长度不相等,字符串不是由旋转得到的
if (strlen(str1) != strlen(str2))
{
return 0;
}
//1.在字符串str1的后面追加一个str1
//追加结果是:AABCDAABCD
int len = strlen(str1);
strncat(str1, str1, len);
//2.判断str2是否为str1的字串
char* ret = strstr(str1, str2);
return ret != NULL;
}
int main()
{
char arr_1[20] = "AABCD";
char arr_2[] = "BCDA";
int ret = is_str_le_rot(arr_1, arr_2);
if (ret == 1)
{
printf("Yes\n");
}
else
{
printf("No\n");
}
return 0;
}
其中 return ret != NULL 等价于
if (ret == NULL)
{
return 0;
}
else
{
return 1;
}