【C语言】OJ题练习

编程练手

  • 二分查找
  • 排序-排序整型数组
  • 数数字
  • 输出99乘法口诀表
  • 判断是否为闰年
  • 判断一个数是否为素数
  • 字符串逆序
  • 求前5项之和
  • 喝汽水问题
  • 调整数组,使得奇数全在偶数前面
  • 打印杨辉三角
  • 判断元音辅音
  • 反向输出一个数字
  • 杨氏矩阵
  • 字符串左旋
  • 判断字符串是否是左旋的结果
  • qsort使用和模拟实现
  • 模拟实现strlen
  • 模拟实现strcat
  • 模拟实现strcmp
  • 模拟实现strcpy
  • 模拟实现strstr
  • 模拟实现memcpy
  • 模拟实现memmove

二分查找

在一个整形有序数组中查找具体的某个数,找到返回下标,找不到返回0

思路:在数组中设置左右下标,若左右下标除以二的结果作为中间下标,若中间下标的值为要找的数字,则返回下标,若不是,则更新左右下标和中间下标,直到找到为止,若左右下标的位置已不包含任何元素,则表示其中没有要找的数字,返回0

int Binary_Search(int* ar, int key, int len)
{
	int left = 0;
	int right = len - 1;
	while(left <= right)//等号不能省去,因为二分查找的时候,中间值可能会直接取到要找的数,去掉后就无法找到要找的数了
	{
		int mid = (left + right) / 2 ;//改变中间下标
		if(ar[mid] > key)//需要改变右边界
			right = mid - 1;
		if(ar[mid] < key)//需要改变左边界
			left = mid + 1 ;
		if(ar[mid] == key)//找到,返回中间下标
			return mid;
	}
	return 0;
}

排序-排序整型数组

一个无序的整型数组,通过冒泡排序的方式排正序/倒序

//冒泡排序
void Bubble_sort(int ar[], int len)
{
	for (int i = 0; i < len - 1; i++)//确定趟数
	{
		for (int j = 0; j < len - 1 - i; j++)//确定每趟对比几对
		{
			if (ar[j] > ar[j + 1])// > 为正序    < 为倒序
			{
				int tmp = ar[j + 1];
				ar[j + 1] = ar[j];
				ar[j] = tmp;
			}
		}
	}
}

数数字

数 1到 100 的所有整数中出现多少个数字9

//数数字

int Calculating_figures(int num, int k)
{
	int res = 0;
	while (num >= k)//当一个整数小于要数的数字,则一定不会再出现那个数字了
	{
		if (num % 10 == k)//模和除可以逐位的查看一个整数每位的数字是多少
			res++;
		num /= 10;
	}
	return res;
}

输出99乘法口诀表

//输出乘法口诀表
int main()
{
	for (int i = 1; i < 10; i++)
	{
		for (int j = 1; j <= i; j++)
		{
			printf("%d * %d = %d  ", j, i, i*j);
		}
		printf("\n");
	}
	return 0;
}

判断是否为闰年

//判断一个数字是否为闰年

int Is_leap_year(int num)
{
		return ((num % 4 == 0) && (num % 100 != 0)) || (num % 400 == 0);//注意,能否整除一个数是%,意为:模x的余数为0,可整除
}

判断一个数是否为素数

//方法一:让循环次数为2-->num/2的范围,减少循环次数
int Is_prime_number(int num)
{
	if (num == 2 || num == 1)
		return 0;//2不是素数, 1也不是素数
	int flag = 1;//标识位,初始值为1,是素数
	for (int i = 2; i <= num / 2 ; i++)
	{
		if (num % i == 0)
		{
			flag = 0;//当if条件成立,则证明不是素数,flag为0
			break;
		}
	}
	return flag;
}
//方法二
//因为偶数一定不是素数,可以先把偶数排除,然后从3-->num/2的范围,并且每次自增2
int Is_prime(int num)
{
	int flag = 1;//1表示是素数
	if (num == 1 || num == 2 || num % 2 == 0)
		return 0;
	for (int i = 3; i < num / 2; i += 2)
	{
		if (num % i == 0)
		{
			flag = 0;
			break;
		}
	}
	return flag;
}
//方法三
//sqrt()函数,求某一个数的平方根,通过计算2-->sqrt(num)之间有无可整除的数字,即可计算出num是否为素数
int Is_prime(int num)
{
	int flag = 1;
	if (num == 1)//0既不是素数也不是合数
		return 0;
	for (int i = 2; i <= sqrt(num); i++)
	{
		if (num % i == 0)
		{
			flag = 0;
			break;
		}
	}
	return flag;
}

字符串逆序

一个字符串,实现逆序

//逆序
char* Reverse(char ar[])
{
	char* par = ar;
	int len = strlen(ar);
	int left = 0;
	int right = len - 1;
	for (int i = 0; i < len / 2; i++)//循环次数应为字符串个数的一半,否则会将已逆序的字符串重新逆序回去
	{
		char tmp = ar[right];
		ar[right] = ar[left];
		ar[left] = tmp;
		right--;
		left++;
	}
	return par;
}

一句英文,实现单词之间逆序,单词本身不逆序

//从指定位置逆序
char* Reverse(char* arbegain, char* arend)
{
	char* parbegain = arbegain;
	while (arbegain < arend)
	{
		char tmp = *arbegain;
		*arbegain = *arend;
		*arend = tmp;
		arbegain++;
		arend--;
	}
	return parbegain;
}

//字符串逆序
char* Reverar(char* ar)
{
	int len = strlen(ar);
	Reverse(ar, ar + len - 1);//整体逆序
	char* parver = ar;//用于返回指针
	char* begain = ar;//用于控制逆序字符串的起始位置
	char* end = ar;//控制逆序字符串的终止位置
	while (*end != '\0')
	{
		while (*end == ' ')
		{
			Reverse(begain, --end);//逆序一个单词
			end += 2;
			begain = end;
		}
		end++;
	}
	return parver;
}

求前5项之和

求Sn=a+aa+aaa+aaaa+aaaaa的前5项之和,其中a是一个数字

//前n项和
int fun(int num, int key)//先产生第n项的数字
{
	int res = 0;
	for(int i = 0; i<key;i++)
	{
		res += num * (int)pow(10, i);
	}
	return res;
}

int sum_add(int num, int key)//计算前n项的和
{
	int tmp = fun(num, key);//产生最大的一个数字
	int res = fun(num, key);
	for (int i = 0; i < key; i++)
	{
		res += tmp / 10;//逐项相加
		tmp /= 10;
	}
	return res;
}

喝汽水问题

1瓶汽水1元,2个空瓶可以换一瓶汽水,给20元,可以喝多少汽水

int bubble_num(int money, int Price)
{
	int bubble = money / Price;//设置初始瓶子的个数
	int empty = bubble;//初始空瓶子数等于瓶子数
	while (empty > 1)
	{
		bubble += empty / 2;//瓶子数累加
		empty = empty / 2 + empty % 2;//空瓶子数计算需要考虑无法除尽
	}
	return bubble;
}

调整数组,使得奇数全在偶数前面

设计两个int变量,一个从数组左端开始右移,一个从右端开始左移,当寻找到左端为偶数并且右端为奇数的时候,两个数字交换,不满足此条件,满足的一端不动,移动不满足要求的一段

void Odd_even(int* ar, int len)
{
	int left = 0; 
	int right = len - 1;
	while (left < right)//数组循环结束条件
	{
		while (ar[left] % 2 == 0 && ar[right] % 2 != 0)//数组找到可以交换的一奇一偶
		{
			int tmp = ar[left];
			ar[left] = ar[right];
			ar[right] = tmp;
		}
		if (ar[left] % 2 != 0)//左端数字不是奇数则不满足交换条件,右端--
			right--;
		if (ar[right] % 2 == 0)//右端数字不满足交换条件,左端++
			left++;
		if (ar[left] % 2 != 0 && ar[right] % 2 == 0)//两端数字都不满足交换条件,左右分别++--
		{
			right--;
			left++;
		}
	}
}

打印杨辉三角

int main()
{
	int ar[8][8] = { 1 };//设置初始为1
	for (int i = 1; i < 8; i++)//行数,从第1行开始
	{
		for(int j = 1; j <= i ; j++)//列数,从第1列开始
		{
			ar[i][0] = 1;杨辉三角的第0列永远为1
			ar[i][j] = ar[i-1][j - 1] + ar[i-1][j];当前元素的值为前一行前一列的值+前一行当前列的值
		}
	}
	for (int i = 0; i < 8; i++)
	{
		for (int j = 0; j <= i; j++)
		{
			printf("%d ", ar[i][j]);
		}
		printf("\n");
	}
	return 0;
}

判断元音辅音

牛客网链接link

反向输出一个数字

牛客网链接link

杨氏矩阵

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

//杨氏矩阵

int search_ar(int ar[][4], int x, int y, int key)//x行数,y列数,key要找的数   int ar[][4]二维数组传参
{
	int i = 0;
	int j = y - 1;//从数组的右上角开始计算
	while (i < x && i >= 0 || j < y && j >= 0)//行数和列数都在数组的范围内
	{
		while (ar[i][j] > key)//当前的数比要找的数大,则一定在下一行
		{
			j--;
		}
		while (ar[i][j] < key)//当前的数比要找的数小,则一定在当前行的前列
		{
			i++;
		}
		if (ar[i][j] == key)//如果找到了,返回1
			return 1;
	}
	return 0;//没找到,返回0
}

字符串左旋

ABCD左旋一个字符得到BCDA

ABCD左旋两个字符得到CDAB

char* ar_loop(char* ar, int time)
{
	char* par = ar;
	int len = strlen(ar);
	while (time)//设置左旋次数
	{
		//每次只旋转一个字符
		char tmp = ar[0];//把当前第一个位置的字符暂存
		for (int i = 0; i < len - 1; i++)
			ar[i] = ar[i + 1];//剩下的字符逐个前移一位
		ar[len-1] = tmp;//把暂存的字符放到\0前
		time--;
	}
	return par;
}

int main()
{
	char ar[] = "abcdef";
	ar_loop(ar, 3);
	printf("%s ", ar);
	return 0;
}

判断字符串是否是左旋的结果

//字符串判断旋转
int Is_loop(char* ar, char* ar1)
{
	int len_ar = (int)strlen(ar);
	int len_ar1 = (int)strlen(ar1);
	if (len_ar != len_ar1)//两个字符串长度不等的话,一定不是旋转得到的,返回0
		return 0;
	for (int time = 0; time < len_ar; time++)//找出所有可能出现的字符串旋转情况
	{
		while (time)//设置左旋次数
		{
			//每次只旋转一个字符
			char tmp = ar[0];//把当前第一个位置的字符暂存
			for (int i = 0; i < len_ar - 1; i++)
				ar[i] = ar[i + 1];//剩下的字符逐个前移一位
			ar[len_ar - 1] = tmp;//把暂存的字符放到\0前
			time--;
		}
		if (strcmp(ar, ar1) == 0)//每种旋转情况出现后进行比较
			return 1;
	}
	return 0;//所有的情况都比较之后,依然没有相等的情况,说明两个字符串没有旋转关系
}

qsort使用和模拟实现

qsort的模拟实现

注意:模拟实现qsort时,依然使用冒泡排序的方式,但是由于无法知道传入的参数是什么类型,因此只能逐字节进行交换,所以my_qsort传入的参数需要有参数个数和参数的字节数,我们只能通过传入的参数字节数来逐字节进行交换,从而实现冒泡排序

//使用冒泡排序的方式进行排序
void my_qsort(void* base, int num, int width, int(* compare)(const void* elem1, const void* elem2))//my_qsort使用冒泡排序实现
{
	for (int i = 0; i < num - 1; i++)//趟数
	{
		for (int j = 0; j < num - i - 1; j++)//每趟比较的对数
		{
			if (compar_int((char*)base+ j*width, (char*)base+ (j+1)*width))//返回值为1,交换两个参数
				//交换
				swap((char*)base + j * width, (char*)base + (j + 1) * width, width);
		}
	}
}
//交换函数
void swap(char* elem1, char* elem2, int width)
{
	while(width)//逐字节进行交换,width代表待排序数组中每个元素所占的字节数
	{
		char tmp = *elem1;
		*elem1 = *elem2;
		elem2 = tmp;
		elem1++;
		elem2++;
		width--;
	}
}
//比较函数
//比较函数是使用my_qsort的人写的,主要目的是告诉my_qsort函数,待排序数组是以什么样的规则进行排序
//例如现在要进行整型的排序
int compar_int(const void* elem1, const void* elem2)
{
	if(*(int*)elem1 > *(int*)elem2)//这里说明待排序数组是整型,以正序的方式进行排序
		return 1;
	else
		return 0;
}
//例如现在要进行short类型排序
int compar_short(const void* elem1, const void* elem2)
{
	if(*(short*)elem1 > *(short*)elem2)
		return 1;
	else
		return 0;
}
//例如现在要进行char类型排序
int compar_char(const void* elem1, const void* elem2)
{
	if(*(char*)elem1 > *(char*)elem2)
		return 1;
	else
		return 0;
}

模拟实现strlen

//模拟实现strlen函数

size_t my_strlen(const char* string)
{
	assert(string != NULL);
	//保护参数
	const char* par = string;
	int count = 0;//设置计数器
	while (*par != '\0')
	{
		count++;
		par++;
	}
	return count;
}

模拟实现strcat

char* my_strcat(char* strDest, char* strsource)
{
	assert(strDest != NULL && strsource != NULL);
	char* par = strDest;
	while (*strDest != '\0')		//第一步,找到被拼接字符串的\0
		strDest++;					//找到了
	while (*strsource != '\0')		//在被拼接字符串的末尾进行拼接
		*strDest++ = *strsource++;	//先拼接,指针后加加
	if (*strsource == '\0')			//最终把\0加到拼接完成的字符串末尾
		*strDest = '\0';
	return par;						//返回最初字符串的开头地址
}

模拟实现strcmp

字符串的比较,是通过字符的ASCII码进行比较的

//方法一
int my_strcmp(const char* array1, const char* array2)
{
	assert(array1 != NULL && array2 != NULL);
	while (*array1 != '\0' && *array2 != '\0' && *array1 == *array2)
	{
		array1++;
		array2++;
		if (*array1 > *array2)
			return 1;
		if (*array1 < *array2)
			return -1;
	}
	//由于存在字符串长度不同的问题,因此需要考虑当字符串长度不同时,如何进行比较
	if (*array1 != '\0')//上面的while循环结束时,可能是由于array2结束了,而array1未结束,则此时array1一定大
		return 1;
	if (*array2 != '\0')//面的while循环结束时,可能是由于array1结束了,而array2未结束,则此时array2一定大
		return -1;
	return 0;//除了while循环中的情况,和长度不一致的情况,都没有判断出大小,说明字符串是相同的
}


//方法二
int my_strcmp(const char* array1, const char* array2)
{
	assert(array1 != NULL && array2 != NULL);
	while (*array1 != '\0' || *array2 != '\0')//由于\0的ASCII码值为0,采用或的方法,当一个字符串结束而另一个字符串未结束的时候,\0与字符一定可以比较出结果,就省去了考虑字符串长度的问题
	{
		if (*array1 == *array2)
		{
			array1++;
			array2++;
		}
		if (*array1 > *array2)
			return 1;
		if (*array1 < *array2)
			return -1;
	}
	//当字符串循环结束都没有比较出结果,说明字符串是一样的
	return 0;
}

模拟实现strcpy

//实现strcpy函数
char* my_strcpy(char* strDestination, const char* strSource)
{
	assert(strDestination != NULL && strSource != NULL);
	while (*strSource != '\0')
	{
		*strDestination = *strSource;
		strSource++;
		strDestination++;
	}
	if (*strSource == '\0')
		*strDestination = '\0';
	return strDestination;
}

int main()
{
	char ar[] = "abcde";
	char ar1[] = "fghi";
	my_strcpy(ar, ar1);
	//strcpy(ar, ar1);
	printf("%s ", ar);
	return 0;
}

模拟实现strstr

//模拟实现strstr
int my_strstr(const char* dest, const char* src)
{
	const char* left = dest;
	const char* end = dest;
	const char* src_tmp = src;
	while (*end != '\0')//设置结束条件
	{
		left = end;//修改起始指针的位置
		src_tmp = src;//讲查找字符串的指针位置置于初始位置
		while (*left != '\0' && *src_tmp != '\0' && *left == *src_tmp)
		{
			left++;
			src_tmp++;
		}
		if (*src_tmp == '\0')//当查找完成后,查找字符串的指针指向\0,说明查找完成,存在字符串
			return 1;
		end++;//被查找字符串中不存在所有查找字符串的字符,上诉循环退出,指针++
	}
	return -1;//整个被查找字符串已查找完成,没有找到字符串的存在,返回-1
}

模拟实现memcpy

memcpy由于传入的参数类型未知,因此需要通过逐字节的方式进行拷贝

//实现memcpy
void* my_memcpy(void* dest, const void* src, size_t count)
{
	void* par = dest;
	while (count)//设置拷贝字节数
	{
		*(char*)dest = *(char*)src;//进行拷贝
		dest = (char*)dest + 1;//设置向后移动一个字节
		src = (char*)src + 1;//设置向后移动一个字节
		count--;
	}
	return par;//返回被拷贝数组的首地址
}

模拟实现memmove

memmove与memcpy的区别:memcpy不能保证自己拷贝自己时发生空间重叠时的正确性,而memmove可以确保正确(也是就是说,memmove可以解决指针指向的空间重叠的问题)

void* my_memmove(void* des, const void* src, size_t num)
{
	assert(des && src);
	void* ret = des;
	char* des_tmp = (char*)des;//目标字符串
	const char* src_tmp = (char*)src;源字符串
 
	if (des_tmp <= src_tmp || src_tmp + num < des_tmp)//情况2和情况1
	{
		while (num--)
		{
			*des_tmp++ = *src_tmp++;
			
		}
	}
	else//情况2
	{
		des_tmp += num - 1;
		src_tmp += num - 1;
		while (num--)
		{
			*des_tmp-- = *src_tmp--;
		}
	}
	return ret;返回目标字符串首地址
}

完整代码在gitee项目:link

你可能感兴趣的:(c语言,c语言,学习,开发语言)