=========================================================================
相关代码gitee自取:
C语言学习日记: 加油努力 (gitee.com)
=========================================================================
接上期:
学C的第二十八天【字符串函数和内存函数的介绍(一)】_高高的胖子的博客-CSDN博客
=========================================================================
(11). memcpy()函数:
从存储区 str2 拷贝n 个字节的数据到存储区 str1
函数返回值类型和相关参数:
void * memcpy ( void * destination, const void * source, size_t num );
(参数接收 任意类型的目标空间地址 和 任意类型的常量源数据地址 和 拷贝的字节数,
返回 任意类型的拷贝完成后的目标空间地址)
注意事项:
(1).
函数memcpy从source的位置开始向后复制num个字节的数据到destination的内存位置。
需要头文件
。
(2).
这个函数在遇到 '\0' 的时候并不会停下来。
(3).
如果source和destination有任何的重叠,复制的结果都是未定义的。
(4).
学会memcpy函数的模拟实现(下面第2个模块有)
(12). memmove()函数:
从存储区 str2 移动 n 个字节的数据到存储区 str1
函数返回值类型和相关参数:
void * memmove ( void * destination, const void * source, size_t num );
(参数接收 任意类型的目标空间地址 和 任意类型的常量源数据地址 和 移动的字节数,
返回 任意类型的移动完成后的目标空间地址)
注意事项:
(1).
和memcpy的差别就是memmove函数处理的源内存块和目标内存块是可以重叠的。
(2).
如果源空间和目标空间出现重叠,就得使用memmove函数处理。
(3).
学会memmove函数的模拟实现(下面第2个模块有)
(13). memcmp()函数:
比较从ptr1和ptr2指针开始的num个字节
函数返回值类型和相关参数:
int memcmp ( const void * ptr1, const void * ptr2, size_t num );
(参数接收 两个任意类型的常量地址 和 比较的字节数,
返回 一个整数)
注意事项:
(1). 标准规定:
第一个元素 大于 第二个元素,则返回 大于0 的数字
第一个元素 等于 第二个元素,则 返回0
第一个元素 小于 第二个元素,则返回 小于0 的数字
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
(6). 模拟实现memcpy()函数:
模拟实现的自定义函数:
主函数:
对应代码:
//模拟memcpy()函数: #include
#include void* my_memcpy(void* dest, const void* src, size_t num) //返回值为 拷贝结束后的目标空间起始地址 //size_t num:参数接收的是 要拷贝的字节数,因为不同类型的数据字节数是不同的 { //保存目标空间的原始地址,方便拷贝完后返回原地址: void* ret = dest; //使用断言,确保两指针不是空指针: assert(dest && src); while (num--) //将两个void*指针,转化为char*指针,一个字节一个字节拷贝 //所以有几个字节就拷贝几次 { //转化为char*指针,一个字节一个字节拷贝: //强制类型转化只是临时的: *(char*)dest = *(char*)src; //移动指针拷贝下一位: //因为void*指针不能++和*, //所以要转化为char*后+1再赋给dest: dest = (char*)dest + 1; src = (char*)src + 1; } //返回原地址: return ret; } int main() { //源数据: int arr1[] = { 1,2,3,4,5,6,7,8,9,10 }; //目标空间: int arr2[20] = { 0 }; //将arr1的内容拷贝到arr2中,因为是int类型不能使用strcpy函数: //使用模拟的自定义函数: my_memcpy(arr2, arr1, 20); //结果: int i = 0; for (i = 0; i < 20; i++) { printf("%d ", arr2[i]); } return 0; }
(7). 模拟实现memmove()函数:
模拟实现的自定义函数:
主函数:
对应代码:
//模拟实现memmove函数: #include
#include void* my_memmove(void* dest, const void* src, size_t num) // 目标空间 原数据空间 (首元素地址) { //保存起始位置: void* ret = dest; //断言: assert(dest && src); //如果源数据空间在目标空间有重叠,则有2种情况: //1. 目标空间首地址(dest) 在 源数据空间首地址左边(src) , // 那就需要 从前往后 进行移动,才不会有源数据被覆盖的情况 //2. 目标空间首地址(dest) 在 源数据空间首地址右边(src) , // 那就需要 从后往前 进行移动,才不会有源数据被覆盖的情况 //如果两个空间没有重叠的情况,则 从前往后 还是 从后往前 移动都可以 //使用分两种情况即可: //void* 不能解引用和+1-1操作,但可以比(地址)大小 if (dest < src) //第一种情况 { //从前往后 移动: while (num--) { *(char*)dest = *(char*)src; dest = (char*)dest + 1; src = (char*)src + 1; } } else //第二种情况 { //从后往前 移动: while (num--) { *((char*)dest + num) = *((char*)src + num); } } return ret; } int main() { //源数据: int arr1[] = { 1,2,3,4,5,6,7,8,9,10 }; my_memmove(arr1, arr1+2, 20); //结果: int i = 0; for (i = 0; i < 10; i++) { printf("%d ", arr1[i]); } return 0; }
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
(1). 杨氏矩阵:
题目:
有一个数字矩阵(二维数组),
矩阵的每行从左到右是递增的,
矩阵从上到下是递增的,
请编写程序在这样的矩阵中查找某个数字是否存在,要求:时间复杂度小于O(N)。
时间复杂度为O(N)的版本:
对应代码:
//正常写法,时间复杂度为O(N): #include
int main() { //给出符合杨氏矩阵的二维数组: int arr[3][3] = { 1,2,3,4,5,6,7,8,9 }; //输入要找的数: int k = 0; scanf("%d", &k); //在二维数组中一个一个遍历:时间复杂度为O(N) int i = 0; for (i = 0; i < 3; i++)//遍历行 { int j = 0; for (j = 0; j < 3; j++)//遍历列 { if (arr[i][j] == k)//如果找到了 { //进行打印: printf("找到了,它的下标为:%d %d", i, j); return 0; //直接使用return结束程序, //break只能跳出一层循环 } } } //未找到则打印未找到: printf("未找到"); return 0; }
时间复杂度小于O(N)的版本:
对应代码:
#include
void young_tableau_search(int arr[3][3], int k, int* px, int* py) { //开始查找,思路: //因为是杨氏矩阵,所以一行中最右边的数是最大的 //这个最大值如果比要找的值都小的话,那就可以排除这一行 //列也是同理 // 坐标(0,2),第一行的最大值 int x = 0; //二维数组的行 int y = *py-1; //二维数组的列 while (x <= *px-1 && y >= 0) //行最大调整到第二行,列最多调整到第0行 { if (arr[x][y] < k) //如果第一行的最大值都小于k, { x++; //排除这一行,移到下一行 } else if (arr[x][y] > k) //如果第一行的最大值大于k, { y--; //说明k可能就在这一行,移动列进行查找 } else { //找到了就把k的行和列赋给指针px和指针py: *px = x; *py = y; return; } } //自定义未找到的情况: *px = -1; *py = -1; } int main() { //给出符合杨氏矩阵的二维数组: int arr[3][3] = { 1,2,3,4,5,6,7,8,9 }; // 1 2 3 // 4 5 6 // 7 8 9 //输入要找的数: int k = 0; scanf("%d", &k); int x = 3; //k的行 int y = 3; //k的列 //自定义一个函数进行查找: young_tableau_search(arr, k, &x, &y); //参数: 二维数组名,要找的值,行数,列数 //通过函数的使用情况打印相应情况: if (x==-1 && y==-1) //未找到: { printf("未找到"); } else //找到了: { printf("找到了,它的下标为:第%d行 第%d列", x, y); } return 0; }
(2). 字符串左旋:
题目:
实现一个函数,可以左旋字符串中的k个字符。
例如:
ABCD左旋一个字符得到BCDA
ABCD左旋两个字符得到CDAB
方法1:左旋一个字符就移动一次剩余的字符
对应代码:
//方法1:左旋一个字符就移动一次剩余的字符 //实现一个函数,可以左旋字符串中的k个字符。 //例如: //ABCD左旋一个字符得到BCDA //ABCD左旋两个字符得到CDAB #include
#include #include void left_move(char* str, int k) { //断言:str不为空指针 assert(str); //左旋k次: int j = 0; for (j = 0; j < k; j++) { //存放被左旋字符的地址,从第一个字符开始 char tmp = *str; //求字符串长度: int len = strlen(str); //左旋后一个字符后,后面len-1个字符要往前挪一位 int i = 0; for (i = 0; i < len - 1; i++) { //把后一个字符赋给前一个 *(str + i) = *(str + i + 1); //这里就覆盖掉了前面一个字符,末尾空出一个字符 } //将左旋的字符移动到尾部 *(str + len - 1) = tmp; } } int main() { char arr[] = "ABCD"; //输入左旋次数: int k = 0; scanf("%d", &k); //使用自定义函数进行左旋: left_move(arr, k); //参数: 字符串首地址,左旋次数 //打印左旋后结果: printf("%s\n", arr); return 0; }
方法2:
把字符串分为两部分,左旋字符为一部分,剩余字符为一部分,分别进行逆序,再整体逆序
对应代码:
//方法2: #include
#include #include //定义一个逆序函数: void reverse(char* left, char* right) { //断言:两指针不为空 assert(left && right); //进行逆序: while (left < right) //两指针中间还有数就继续 { char tmp = *left; *left = *right; *right = tmp; left++; right--; } } void left_move(char* str, int k) { int len = strlen(str); k %= len;//防止重复操作 //求左旋字符进行逆序: reverse(str, str + k - 1); //对剩余字符进行逆序: reverse(str + k, str + len - 1); //整体逆序: reverse(str, str + len - 1); } int main() { char arr[] = "ABCD"; //输入左旋次数: int k = 0; scanf("%d", &k); //使用自定义函数进行左旋: left_move(arr, k); //参数: 字符串首地址,左旋次数 //打印左旋后结果: printf("%s\n", arr); return 0; }
(3). 字符串旋转结果:
题目:
写一个函数,判断一个字符串是否为另外一个字符串旋转之后的字符串。
例如:
给定s1 =AABCD和s2 = BCDAA,返回1
给定s1=abcd和s2=ACBD,返回0.
AABCD左旋一个字符得到ABCDA
AABCD左旋两个字符得到BCDAA
AABCD右旋一个字符得到DAABC
方法1:
str1每旋转一次就和str2进行对比,不同则继续进行下次旋转
对应代码:
#include
#include int is_left_move(char* str1, char* str2) { //求字符串长度: int len = strlen(str1); int j = 0; for (j = 0; j < len; j++) //字符串长度为几,最多就旋转几次 { //存放被左旋字符的地址,从第一个字符开始 char tmp = *str1; //左旋后一个字符后,后面len-1个字符要往前挪一位 int i = 0; for (i = 0; i < len - 1; i++) { //把后一个字符赋给前一个 *(str1 + i) = *(str1 + i + 1); //这里就覆盖掉了前面一个字符,末尾空出一个字符 } //将左旋的字符移动到尾部 *(str1 + len - 1) = tmp; //到这里就旋转了1次, //每旋转1次就使用strcmp判断是否和str2相同: if (strcmp(str1, str2) == 0) { return 1;//找到返回1 } } //未找到就返回0 return 0; } int main() { char arr1[] = "ABCDEF"; char arr2[] = "CDEFAB"; //使用自定义函数: int ret = is_left_move(arr1, arr2); //根据自定义函数返回结果进行打印: if (ret == 1) { printf("Yes\n"); } else { printf("No\n"); } return 0; }
方法2:
把str1变成 “str1+str1”,如果 “str1+str1” 中有str2,那就可以通过str1旋转得到str2
对应代码:
#include
#include int is_left_move(char* str1, char* str2) { int len1 = strlen(str1); int len2 = strlen(str2); //判断两字符串长度是否相同,不同的话肯定不能通过旋转得到: if (len1 != len2) { return 0; } //通过strncat在str1上再追加str1: strncat(str1, str1, len1); //判断追加后的str1中有没有str2,有则可以通过旋转得到: if (strstr(str1, str2) == NULL) { return 0; } else { return 1; } } int main() { char arr1[20] = "ABCDEF"; char arr2[] = "CDEFAB"; //使用自定义函数: int ret = is_left_move(arr1, arr2); //根据自定义函数返回结果进行打印: if (ret == 1) { printf("Yes\n"); } else { printf("No\n"); } return 0; }