学C的第二十九天【字符串函数和内存函数的介绍(二)】

=========================================================================

相关代码gitee自取

C语言学习日记: 加油努力 (gitee.com)

 =========================================================================

接上期

学C的第二十八天【字符串函数和内存函数的介绍(一)】_高高的胖子的博客-CSDN博客

 =========================================================================

                        

1 . 函数介绍(续)

(11). memcpy()函数:

从存储区 str2 拷贝n 个字节的数据到存储区 str1

               

函数返回值类型和相关参数:

             

void * memcpy ( void * destination, const void * source, size_t num );

               

(参数接收 任意类型的目标空间地址 任意类型的常量源数据地址拷贝的字节数

返回 任意类型的拷贝完成后的目标空间地址

                    

注意事项:

(1).

函数memcpy从source的位置开始向后复制num个字节的数据到destination的内存位置

需要头文件<string.h>

                  

(2).

这个函数在遇到 '\0' 的时候并不会停下来

                

(3).

如果sourcedestination有任何的重叠复制的结果都是未定义的

               

(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 的数字

         

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

             

2 . 库函数的模拟实现(续)

(6). 模拟实现memcpy()函数:

模拟实现的自定义函数:

                  

主函数:

                       

对应代码:

//模拟memcpy()函数:
#include <stdio.h>
#include <assert.h>

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 <stdio.h>
#include <assert.h>

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;
}

                   

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

                     

3 . 练习

(1). 杨氏矩阵:

          

题目:

有一个数字矩阵二维数组),

矩阵的每行从左到右是递增的
矩阵从上到下是递增的
请编写程序在这样的矩阵中查找某个数字是否存在,

要求:时间复杂度小于O(N)

                     

时间复杂度为O(N)的版本:

        

对应代码:

//正常写法,时间复杂度为O(N):
#include <stdio.h>
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 <stdio.h>

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 <stdio.h>
#include <string.h>
#include <assert.h>

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 <stdio.h>
#include <assert.h>
#include <string.h>

//定义一个逆序函数:
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 =AABCDs2 = BCDAA返回1

给定s1=abcds2=ACBD返回0.

            

AABCD左旋一个字符得到ABCDA

AABCD左旋两个字符得到BCDAA

AABCD右旋一个字符得到DAABC

             

方法1:

str1每旋转一次就和str2进行对比,不同则继续进行下次旋转

                   

对应代码:

#include <stdio.h>
#include <string.h>
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 <stdio.h>
#include <string.h>
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;
}

猜你喜欢

转载自blog.csdn.net/weixin_63176266/article/details/131757093