C语言编程题(一)

文章目录

题1:求两个数的较大值

#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
 
int Max(int num1,int num2)
{
  if (num1 >= num2)
  {
    return num1;
  }
  else
  {
    return num2;
  }

  //return num1 > num2 ? num1 : num2;
}
 
int main()
{
  int a = 0;
  int b = 0;
  int max = 0;
  scanf("%d%d",&a,&b);
  max = Max(a,b);
  printf("较大值为:%d\n",max);
  return 0;
}

题2:写代码将三个整数按从大到小输出

例如:
输入:2 3 1
输出:3 2 1

int main()
{
	int a, b, c;
	scanf("%d%d%d",&a,&b,&c);

	int tmp = 0;
	if (a < b)
	{
		tmp = a;
		a = b;
		b = tmp;
	}

	if (a < c)
	{
		tmp = c;
		a = c;
		c = tmp;
	}

	if (b < c)
	{
		tmp = b;
		b = c;
		c = tmp;
	}
	printf("%d %d %d", a, b, c);
	return 0;
}

题3:写一个代码打印1-100之间所有3的倍数的数字

#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
 
//写一个代码打印1-100之间所有3的倍数的数字
//方法1
void func1()
{	
	int i = 1;
	for (i = 3; i <= 100; i += 3)
	{
		printf("%d\n", i);
	}
}
//方法2 
void func2()
{
	int i = 3;
	while (i < 100)
	{
		if (0 == i % 3)
		{
			printf("%d\n",i);
		}
		i++;
	}
}

int main()
{
	//func1();
	func2();
	return 0;
}

题4:给定两个数,求这两个数的最大公约数

例如:
输入:20 40
输出:20

1.试除法/暴力求解法

int main()
{ 
	int a, b;
	scanf("%d%d",&a,&b);

	int tmp = 0;
	if (a < b)
	{
		tmp = a;
		a = b;
		b = tmp;
	}

	int i = b;
	for (i = b;i>0;i--)
	{
		if ( (0 == a % i)&&(0 == b % i) )
		{
			printf("最大公约数是%d\n",i);
			break;
		}
	}
	return 0;
}

2.辗转相除法

int main()
{
	int a, b;
	scanf("%d%d", &a, &b);

	int r = 0;
	//a  b
	//18 24   -余18
	//24 18   -余6
	//18 6    -0
	//最大公约数为6
	while (r = a % b)
	{
		a = b;
		b = r;
	}
	printf("最大公约数是%d\n", b);

	return 0;
}

题5:求解两个数的最小公倍数

思路:
1.暴力求解,求出两个数的较大值,对该数递增,逐一试
2.利用最大公约数,最小公倍数= a * b / 最大公约数

1.暴力求解

int main()
{
	int a, b;
	scanf("%d %d",&a, &b);

	int tmp;
	if (a < b)
	{
		tmp = a;
		a = b;
		b = tmp;
	}

	while (1)
	{
		if ((tmp % a == 0) && (tmp % b == 0))
		{
			printf("最大公约数是%d\n",tmp);
			break;
		}
		tmp++;
	}
	return 0;
}

2.利用最大公约数求解

int maxCommonDivisor(int a,int b)
{
	int r = 0;
	while (r = a % b)
	{
		a = b;
		b = r;
	}
	return b;
}

int main()
{
	int a, b;
	scanf("%d %d",&a, &b);
	int ret = maxCommonDivisor(a, b);

	printf("最小公倍数是%d\n",a * b / ret);

	return 0;
}

题6:打印1000年到2000年之间的闰年

闰年满足如下条件之一:
1.能被4整除,并且不能被100整除
2.能被400整除

//1.
int main()
{
	int i = 1000;
	for (i = 1000; i < 2000; i++)
	{
		if (((i % 4) == 0) && ((i % 100) != 0))
		{
			printf("%d ",i);
		}
		else if ((i % 400) == 0)
		{
			printf("%d ",i);
		}
	}
	return 0;
}

//2.
int main()
{
	int i = 1000;
	for (i = 1000;i < 2000; i++)
	{
		if ( ((i % 4) == 0) && ((i % 100) != 0) )
		{
			printf("%d ",i);
		}
		if ((i % 400) == 0)
		{
			printf("%d ",i);
		}
	}
	return 0;
}

//3.
int main()
{
	int i = 1000;
	for (i = 1000; i < 2000; i++)
	{
		if (((i % 4) == 0) && ((i % 100) != 0) || ((i % 400) == 0))
		{
			printf("%d ", i);
		}
	}
	return 0;
}

//这种写法错误
int main()
{
	int i = 1000;
	for (i = 1000;i < 2000; i++)
	{
		if ((i % 4) == 0)
		{
			//一旦进入这里,如果不满足(i % 100) != 0,那么就不会执行else中的语句
			if ((i % 100) != 0)
			{
				printf("%d ",i);
			}
			
		}
		else if ((i % 400) == 0)
		{
			printf("%d ",i);
		}
	}
	return 0;
}

题7:写一个代码:打印100~200之间的素数

//1.
void func()
{
	//只能被1和自身整除
	int i = 101;
	for (i = 101; i < 200; i++)
	{
		int j = 2;
		for (j = 2; j < i; j++)
		{
			if (0 == i % j)
			{
				break;
			}
		}
		if (i == j)
		{
			printf("%d ", i);
		}
	}
}

int main()
{
	func();
	return 0;
}
//2.
void func()
{
	int i = 101;
	
	for (i = 101; i < 200; i++)
	{
		int flag = 0;
		int j = 2;
		for (j = 2; j < i; j++)
		{
			if (0 == i % j)
			{
				flag = 1;
				break;
			}
		}
		if (flag == 0)
		{
			//打印素数
			printf("%d ", i);
		}
	}
}

int main()
{
	func();
	return 0;
}

如果一个数不是素数,那么这个数一定可以拆成两个整数相乘的形式,num = a*b,a和b中必定有一个是小于等于sqrt(num)的,所以我们只需要判断,num能否被2~sqrt(num)之间的数整除即可。

//3.优化
int func()
{
	int i = 101;
	
	for (i = 101; i <200; i++)
	{
		int flag = 0;
		int j = 2;
		for (j = 2; j <= sqrt(i); j++)
		{
			if (0 == i % j)
			{
				flag = 1;
				break;
			}
		}
		if (flag == 0)
		{
			//打印素数
			printf("%d ", i);
		}
	}
}

int main()
{
	func();
	return 0;
}
//4.先剔除所有的偶数-步长控制
int func()
{
	int i = 101;

	for (i = 101; i < 200; i+=2)
	{
		int flag = 0;
		int j = 2;
		for (j = 2; j <= sqrt(i); j++)
		{
			if (0 == i % j)
			{
				flag = 1;
				break;
			}
		}
		if (flag == 0)
		{
			//打印素数
			printf("%d ", i);
		}
	}
}

int main()
{
	func();
	return 0;
}

题8:编写程序数一下 1到 100 的所有整数中出现多少个数字9

int main()
{
	int i = 1;
	int count = 0;
 
	for (i = 1;i <= 100;i++)
	{
		if (9 == i % 10)
		{
			count++;
		}
		if (9 == i / 10)
		{
			count++;
		}
	}
	printf("%d\n",count);
	return 0;
}

题9:计算1/1-1/2+1/3-1/4+1/5 …… + 1/99 - 1/100 的值,打印出结果

int main()
{
	//计算1/1-1/2+1/3-1/4+1/5 …… + 1/99 - 1/100 的值,打印出结果
	int i = 1;
	float sum = 0;
	for (i = 1; i <= 100; i++)
	{
		if (i % 2)
		{
			sum += 1.0 / i;
		}
		else
		{
			sum -= 1.0 / i;
		}		
	}
	printf("%f\n",sum);
	return 0;
}
int main()
{
	int i = 1;
	float sum = 0.0;
	int flag = 1;

	for (i = 1; i <= 100; i++)
	{
		sum += flag * 1.0 / i;
		flag = -flag;
	}
	printf("%f\n", sum);
	return 0;
}

题10:求10 个整数中最大值

int main()
{
	//求10 个整数中最大值
	int arr[10] = {0};
	int i = 0;
	for (i = 0; i < 10; i++)
	{
		scanf("%d",&arr[i]);
	}
	int max = arr[0];
	for (i = 1; i < 10; i++)
	{
		if (arr[i] > max)
		{
			max = arr[i];
		}
	}
	printf("%d\n",max);
	return 0;
}

题11:在屏幕上输出9*9乘法口诀表

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

题12:编写代码在一个整形有序数组中查找具体的某个数

要求:找到了就打印数字所在的下标,找不到则输出:找不到。

int findIndex(int len, int arr[], int k)
{
	int left = 0;
	int right = len - 1;
	int mid = 0;
 
 
	while (left <= right)
	{
		mid = (left + right) / 2;
 
 
		if (k > arr[mid])
		{
			left = mid + 1;
		}
		else if (k < arr[mid])
		{
			right = mid - 1;
		}
		else
		{
			//找到了,返回下标
			return mid;
		}
	}
	//没有找到
	return -1;
}
 
 
int main()
{
	int arr[10] = { 2,5,8,10,14,17,20,24,30,36 };
	int len = sizeof(arr) / sizeof(arr[0]);
	int k = 0;
	printf("请输入要查找的数字:>");
	scanf("%d", &k);
	int index = findIndex(len,arr,k);
 
	if (index != -1)
	{
		printf("找到了,下标为%d\n",index);
	}
	else
	{
		printf("没有找到\n");
	}	
	return 0;
}

题13:猜数字游戏

rand函数的用法:
1.rand函数,每次程序重新运行时,生成的数字都是相同的序列
2.所以在使用rand函数之前,先使用srand函数,初始化随机数生成器/设置随机数的起点,并且在整个程序中只需要设置一次即可
3.srand函数的参数要求是变化的数字(无符号整型),否则生成的数字是一个固定的数,一般拿时间戳作为srand函数的参数
rand函数生成随机数的范围是0~32767.

#include <stdio.h>
#include <stdlib.h>
#include <time.h>

void menu()
{
	printf("***********************************\n");
	printf("*********1.play     0.exit*********\n");
	printf("***********************************\n");
}

void play()
{
	int randomNumber = rand() % 100 + 1;//生成随机数的范围是1-100

	while (1)
	{
		printf("请输入猜测的数字:>");
		int guessNumber = 0;
		scanf("%d", &guessNumber);

		if (guessNumber == randomNumber)
		{
			printf("猜对啦!\n");
			break;
		}
		else if (guessNumber > randomNumber)
		{
			printf("猜大啦!\n");
		}
		else
		{
			printf("猜小啦!\n");
		}
	}
}

int main()
{	
	int input = 0;
	srand((unsigned int)time(NULL));

	do 
	{
		menu();
		printf("请选择:");
		scanf("%d",&input);
		switch (input)
		{
		case 1:
			play();
			break;
		case 0:
			printf("游戏结束!\n");
			break;
		default:
			printf("输入有误!");
			break;
		}
	} while (input);

	return 0;
}

题14:实现一个函数,判断一个数是不是素数。

利用上面实现的函数打印100到200之间的素数。

#include <stdio.h>
#include <math.h>
int is_prime(int n)
{
	int i = 0;
	int flag = 0;
	for (i = 2; i <= sqrt(n); i++)
	{
		if (0 == n % i)
		{
			flag = 1;
			break;
		}
	}
	if (flag == 1)
	{
		return 0;
	}
	else
	{
		return 1;
	}	
}

int main()
{
	int n = 101;
	int ret = is_prime(n);

	if (1 == ret)
	{
		printf("%d是素数\n",n);
	}
	else
	{
		printf("%d不是素数\n",n);
	}
	return 0;
}

题15:实现一个函数来交换两个整数的内容。

void swap(int* a,int* b)
{
	int tmp = *a;
	*a = *b;
	*b = tmp;
}

int main()
{
	int a = 10;
	int b = 20;
	swap(&a,&b);
	printf("%d %d\n",a,b);
	return 0;
}

题16:递归方式实现打印一个整数的每一位

#include <stdio.h>
void print(int n)
{
	while (n)
	{
		printf("%d ",n % 10);
		n /= 10;
	}
}

int main()
{
	int n = 0;
	scanf("%d",&n);
	print(n);
	return 0;
}

题17:递归和非递归分别实现求n的阶乘(不考虑溢出的问题)

非递归:

int Fac(int n)
{
	int ret = 1;
	int i = 0;
	for (i = 1; i <= n; i++)
	{
		ret *= i;
	}
	return ret;
}

int main()
{
	int n = 0;
	scanf("%d",&n);

	int ret = Fac(n);
	printf("%d",ret);

	return 0;
}

递归:

int Fac(int n)
{
	if (n < 2)
	{
		return 1;
	}
	else
	{
		return n * Fac(n - 1);
	}
}


int main()
{
	int n = 0;
	scanf("%d",&n);

	int ret = Fac(n);
	printf("%d",ret);

	return 0;
}

题18:递归和非递归分别实现strlen

非递归:

#include <stdio.h>
int my_strlen(char* ps)
{
	int count = 0;
	while (*ps != '\0')
	{
		count++;
		ps++;
	}
	return count;
}


int main()
{
	char str[] = "abcdef";
	int len = my_strlen(str);
	printf("%d\n",len);
	return 0;
}
int my_strlen(char* ps)
{
	char* p = ps;
	while (*ps!='\0')
	{
		ps++;
	}
	return ps - p;
}
int main()
{
	char str[] = "abcdef";
	int len = my_strlen(str);
	printf("%d\n",len);
	return 0;
}

递归:

int my_strlen(char* ps)
{
	if (*ps!='\0')
	{
		return 1 + my_strlen(++ps);
	}
	else
	{
		return 0;
	}
}

int main()
{
	char str[] = "abcdef";
	int len = my_strlen(str);
	printf("%d\n",len);
	return 0;
}

题19:编写一个函数 reverse_string(char * string)(递归实现)

实现:将参数字符串中的字符反向排列,不是逆序打印。

要求:不能使用C函数库中的字符串操作函数。

比如:char arr[] = “abcdef”;
逆序之后数组的内容变成:fedcba

非递归:

思路:
逆置字符串,循环的方式实现非常简单

  1. 给两个指针,left放在字符串左侧,right放在最后一个有效字符位置(或者左右下标)
  2. 交换两个指针位置上的字符
  3. left指针往后走,right指针往前走,只要两个指针没有相遇,继续2,两个指针相遇后,逆置结束
int my_strlen(char* ps)
{
	if (*ps!='\0')
	{
		return 1 + my_strlen(++ps);
	}
	else
	{
		return 0;
	}
}

void reverse_string(char* str)
{
	int left = 0;
	int right = my_strlen(str) -1;

	while (left <= right)
	{
		char tmp = *(str + left);
		*(str + left) = *(str + right);
		*(str + right) = tmp;
		left++;
		right--;
	}
}

int main()
{
	char str[] = "abcdefg";
	reverse_string(str);
	printf("%s\n",str);
	return 0;
}

递归:

对于字符串“abcdefg”,递归实现的大概原理:

  1. 交换a和g,
  2. 以递归的方式逆置源字符串的剩余部分,剩余部分可以看成一个有效的字符串,再以类似的方式逆置
int my_strlen(char* ps)
{
	if (*ps!='\0')
	{
		return 1 + my_strlen(++ps);
	}
	else
	{
		return 0;
	}
}
//递归
void reverse_string(char* str)
{
	int len = my_strlen(str);

	char tmp = *str;

	*str = *(str + len - 1);

	*(str + len - 1) = '\0';

	if (my_strlen(str+1) > 1 )
	{
		reverse_string(str+1);
	}

	*(str + len - 1) = tmp;
}


int main()
{
	char str[] = "abcdefg";
	reverse_string(str);
	printf("%s\n",str);
	return 0;
}

题20:写一个递归函数DigitSum(n),输入一个非负整数,返回组成它的数字之和

例如,调用DigitSum(1729),则应该返回1+7+2+9,它的和是19

输入:1729,输出:19

int DigitSum(int n)
{
	if (n > 9)
	{
		//最低位+其他位
		return  n % 10 + DigitSum(n / 10);		
	}
	else
	{
		return n;
	}
}

int main()
{
	int n = 0;
	scanf("%d",&n);

	int sum = DigitSum(n);
	printf("%d\n",sum);
	return 0;
}

题21:编写一个函数实现n的k次方,使用递归实现。

非递归:

double pow(int n,int k)
{
	int i = 0;
	double ret = 1.0;
	for (i = 0; i < k; i++)
	{
		ret *= n;
	}
	return ret;
}


int main()
{
	int n = 0;
	int k = 0;
	scanf("%d %d",&n,&k);
	double ret = pow(n,k);
	printf("%lf\n",ret);
	return 0;
}

递归:

double pow(int n,int k)
{
	//n^k = n^(k-1)*n
	if (0 == k)
	{
		return 1;
	}
	else if (k > 0)
	{
		return n * pow(n, k - 1);
	}
	else
	{
		return 1.0 /pow(n, -k);
	}
}
int main()
{
	int n = 0;
	int k = 0;
	scanf("%d %d",&n,&k);
	double ret = pow(n,k);
	printf("%lf\n",ret);
	return 0;
}

题22:递归和非递归分别实现求第n个斐波那契数

例如:

输入:5 输出:5

输入:10, 输出:55

输入:2, 输出:1

非递归:

int Fib(int n)
{
	int a = 1;
	int b = 1;
	int c = 0;

	if (n <= 2)
	{
		return 1;
	}

	int i = 3;
	while (i <=n)
	{
		c = a + b;
		a = b;
		b = c;
		i++;
	}
	return c;
}
int main()
{
	int n = 0;
	scanf("%d",&n);

	int ret = Fib(n);
	printf("%d\n",ret);

	return 0;
}

递归:
一个问题直接求解时不好求解,如果可以将其划分成其子问题,并且子问题和原问题有相同的解法时,就可以使用递归的方式解决
递归的两个条件:

  1. 将问题划分成其子问题,要求:子问题要与原问题具有相同的解法
  2. 递归的出口
int Fib(int n)
{
	if (n <= 2)
	{
		return 1;
	}
	return Fib(n - 1) + Fib(n-2);
}

int main()
{
	int n = 0;
	scanf("%d",&n);

	int ret = Fib(n);
	printf("%d\n",ret);

	return 0;
}

题23:实现一个对整形数组的冒泡排序

#include<stdio.h>
void BubbleSort(int *arr,int len)
{
	//趟数
	int i = 0;
	for (i = 0; i < len - 1; i++)
	{
		//每趟比较的对数
		int j = 0;
		int tmp = 0;
		int flag = 1;

		for (j = 0; j < len - 1 - i; j++)
		{
			if (arr[j] > arr[j + 1])
			{
				tmp = arr[j];
				arr[j] = arr[j + 1];
				arr[j + 1] = tmp;
				flag = 0;
			}
		}
		if (flag == 1)
		{
			break;
		}
	}
}

int main()
{
	int arr[] = {10,3,8,1,2,7,9,6,4,5};

	int len = sizeof(arr) / sizeof(arr[0]);

	BubbleSort(arr, len);

	int i = 0;
	for (i = 0; i < len; i++)
	{
		printf("%d ",arr[i]);
	}

	return 0;
}

题24:将数组A中的内容和数组B中的内容进行交换。(数组一样大)

#include <stdio.h>

int main()
{
	int arrA[] = {1,2,3,4,5,6,7,8,9,10};
	int arrB[] = {11,12,13,14,15,16,17,18,19,20};

	int len = sizeof(arrA) / sizeof(arrA[0]);

	int tmp = 0;
	int i = 0;
	for(i = 0; i < len; i++)
	{
		tmp = arrA[i];
		arrA[i] = arrB[i];
		arrB[i] = tmp;
	}

	for(i = 0; i < len; i++)
	{
		printf("%d ",arrA[i]);
	}

	for(i = 0; i < len; i++)
	{
		printf("%d ",arrB[i]);
	}

	return 0;
}

题25:创建一个整形数组,完成对数组的操作

实现函数init() 初始化数组为全0
实现print() 打印数组的每个元素
实现reverse() 函数完成数组元素的逆置。
要求:自己设计以上函数的参数,返回值。

#include <stdio.h>
void Init(int *arr,int len)
{
	int i = 0;
	for (i = 0; i < len; i++)
	{
		arr[i] = 0;
	}
}

void print(int *arr,int len)
{
	int i = 0;
	for (i = 0; i <len;i++)
	{
		printf("%d ",arr[i]);
	}
}

void reverse(int *arr,int len)
{
	int left = 0;
	int right = len - 1;
	int tmp = 0;

	while (left <= right)
	{
		tmp = arr[left];
		arr[left] = arr[right];
		arr[right] = tmp;
		left++;
		right--;
	}
}

int main()
{
	int arr[10];
	int len = sizeof(arr) / sizeof(arr[0]);
	
	Init(arr,len);


	int i = 0;
	for (i = 0; i < len; i++)
	{
		arr[i] = i;
	}

	print(arr,len);
	printf("\n");

	reverse(arr,len);

	print(arr, len);
	return 0;
}

题26:求两个数二进制中不同位的个数

编程实现:两个int(32位)整数m和n的二进制表达中,有多少个位(bit)不同?

输入例子:

1999 2299

输出例子:7

1.直接比较两个数不同位的个数

int main()
{
	int n = 0;
	int m = 0;
	scanf("%d %d", &n, &m);

	int i = 0;
	int count = 0;
	for (i = 0; i < 32; i++)
	{
		if ( ((n >>i)&1) != ((m>>i)&1) )
		{
			count++;
		}
	}
	printf("%d\n",count);
	return 0;
}

2.两个数进行异或,计算异或得到的结果中1的个数

int fun(int a, int b)
{
	int c = a ^ b;
	//00000000 00000000 00000000 00011110
	int count = 0;

	int i = 0;
	for (i = 0; i < 32; i++)
	{
		if (1 == ((c>>i)&1))
		{
			count++;
		}
	}
	return count;
}


int main()
{
	int a = 10;
	//00000000 00000000 00000000 00001010

	int b = 20;
	//00000000 00000000 00000000 00010100

	int ret = fun(a,b);
	printf("%d\n",ret);

	return 0;
}
/*
思路:
1. 先将m和n进行按位异或,此时m和n相同的二进制比特位清零,不同的二进制比特位为1
2. 统计异或完成后结果的二进制比特位中有多少个1即可
*/
#include <stdio.h>
int count_bit_ont(int n)
{
	int count = 0;
	while (n)
	{
		count++;
		n = n & (n-1);
	}
	return count;
}

int count_bit_diff(int n,int m)
{
	int a = n ^ m;
	int count = count_bit_ont(a);
	return count;
}
 
 
int main()
{
 int m,n;
 while(scanf("%d %d", &m, &n) == 2)
 {
     printf("%d\n", calc_diff_bit(m, n));
 }
 return 0;
}

题27:打印整数二进制的奇数位和偶数位

获取一个整数二进制序列中所有的偶数位和奇数位,分别打印出二进制序列

/*
思路:
1. 提取所有的奇数位,如果该位是1,输出1,是0则输出0
2. 以同样的方式提取偶数位置
 
 
 检测num中某一位是0还是1的方式:
   1. 将num向右移动i位
   2. 将移完位之后的结果与1按位与,如果:
      结果是0,则第i个比特位是0
      结果是非0,则第i个比特位是1
*/
void Printbit(int num)
{
	for(int i=31; i>=1; i-=2)
	{
		printf("%d ", (num>>i)&1);
	}
	printf("\n");
    
	for(int i=30; i>=0; i-=2)
	{
		printf("%d ", (num>>i)&1);
	}
	printf("\n");
}

题28:统计二进制中1的个数

写一个函数返回参数二进制中 1 的个数。

比如: 15 0000 1111 4 个 1

方法1:

#include <stdio.h>
int count_bit_ont(int n)
{
	int count = 0;
	int i = 0;
	for (i = 0; i < 32; i++)
	{
		if (1 == ((n >> i) & 1))
		{
			count++;
		}
	}
	return count;
}

int main()
{
	int n = 0;
	scanf("%d", &n);
	int count = count_bit_ont(n);
	printf("%d\n", count);
	return 0;
}

方法2:

#include <stdio.h>
int count_bit_ont(int n)
{
	int count = 0;
	while (n)
	{
		count++;
		n = n & (n - 1);
	}
	return count;
}

int main()
{
	int n = 0;
	scanf("%d", &n);
	int count = count_bit_ont(n);
	printf("%d\n", count);
	return 0;
}

题29:交换两个变量(不创建临时变量)

不允许创建临时变量,交换两个整数的内容

#include <stdio.h>
int main()
{
	int a = 10;
    int b = 20;
    printf("交换前:a = %d b = %d\n", a,b);
    a = a^b;
    b = a^b;
    a = a^b;
    printf("交换后:a = %d b = %d\n", a,b);
	return 0;
}

题30:使用指针打印数组内容

写一个函数打印arr数组的内容,不使用数组下标,使用指针。
arr是一个整形一维数组。

#include <stdio.h>
int main()
{
    int arr[] = {1,2,3,4,5,6,7,8,9,10};
    //在这里完成代码
    //分析:因为数组中存储的元素类型是int类型的,因此只要给一个int的指针,依次取索引数组中的每个元素即可
    int* p = arr;  // 数组名代表数组首元素的地址
    for(int i = 0; i < sizeof(arr)/sizeof(arr[0]); ++i)
    {
        printf("%d ", *p);   // *p: 取到p所指向位置的元素
        ++p;                 // 获取p的下一个位置
    }
    
   	return 0;
}

题31:计算求和

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

例如:2+22+222+2222+22222

/*
思路:
通过观察可以发现,该表达式的第i项中有i个a数字,因此:
假设第i项为temp,则第i+1项为temp*10+a
具体参考以下代码
*/
int main()
{
	int a = 0;
	int n = 0;
	int i = 0;
	int sum = 0;
	int tmp = 0;
 
 
	scanf("%d%d", &a, &n);
	for(i=0; i<n; i++)
	{
		tmp = tmp*10+a;
		sum += tmp;
	}
	printf("%d\n", sum);
 
	return 0;
}

题32:打印水仙花数

求出0~100000之间的所有“水仙花数”并输出。

“水仙花数”是指一个n位数,其各位数字的n次方之和确好等于该数本身,如:153=13+53+3^3,则153是一个“水仙花数”。

/*
思路:
此题的关键在于只要知道判断一个数据是否为水仙花数的方式,问题就迎刃而解。假定给定一个数据data,具体检测方式如下:
1. 求取data是几位数
2. 获取data中每个位置上的数据,并对其进行立方求和
3. 对data中每个位上的数据立方求和完成后,在检测其结果是否与data相等即可,
相等:则为水仙花数
否则:不是
具体实现参考以下代码。
*/
int main()
{
	int i = 0;
	for(i=0; i<=999999; i++)
	{
		int count = 1;
		int tmp = i;
		int sum = 0;
		//判断i是否为水仙花数
		//1. 求判断数字的位数
		while(tmp/10)
		{
			count++;
			tmp = tmp/10;
		}
     
		//2. 计算每一位的次方和
		tmp = i;
		while(tmp)
		{
			sum += pow(tmp%10, count);
			tmp = tmp/10;
		}
     
		//3. 判断
		if(sum == i)
			printf("%d ", i);
	}
	return 0;
}

题33:字符串逆序

写一个函数,可以逆序一个字符串的内容。

代码1

#include <stdio.h>
#include <string.h>

void reverse_string(char* str)
{
	int len = strlen(str);
	int right = len - 1;
	int left = 0;

	char tmp = 0;
	while (left <= right)
	{
		tmp = str[left];
		str[left] = str[right];
		str[right] = tmp;
		left++;
		right--;
	}
}
int main()
{
	char str[] = "abcdefgh";
	reverse_string(str);
	printf("%s\n",str);
	return 0;
}

代码2

void reverse_string(char* str)
{
	int len = strlen(str);

	char* left = str;
	char* right = str + len - 1;

	char tmp = 0;
	while (left <= right)
	{
		tmp = *left;
		*left = *right;
		*right = tmp;
		left++;
		right--;
	}
}

int main()
{
	char str[] = "abcdefgh";

	reverse_string(str);

	printf("%s\n",str);

	return 0;
}

若要求输入字符串,使用gets函数获取字符串并存储在字符数组中

#include <stdio.h>
#include <string.h>
void reverse_string(char* str)
{
    int len = strlen(str);
    int left = 0;
    int right = len -1;
    char tmp = 0;
    while(left <= right)
    {
        tmp = str[left];
        str[left] = str[right];
        str[right] = tmp;
        left++;
        right--;
    }
}


int main()
{
    char str[100];
    gets(str);
    reverse_string(str);
    printf("%s\n",str);
    return 0;
}

若要求循环输入字符串,那么每次需要把缓存区数据置0,待下次重新输入

int main()
{
    char str[101] = {0};
    while(gets(str))
    {
    	//逆序字符串
        Reverse(str);
        printf("%s\n", str);
        //缓存区置0
        memset(str, 0, sizeof(str)/sizeof(str[0]));
    }
    return 0;
}

题34:打印菱形

用C语言在屏幕上输出以下图案:
在这里插入图片描述

行号      空格个数:         *个数:
 0         6                 1
 1         5                 3
 2         4                 5
 3         3                 7
 4         2                 9
 5         1                 11      
 6         0                 13
 0         1                 11
 1         2                 9
 2         3                 7
 3         4                 5
 4         5                 3
 5         6                 1

/*
思路:
仔细观察图形,可以发现,此图形中是由空格和*按照不同个数的输出组成的。
上三角:先输出空格,后输出*,每行中
  空格:从上往下,一行减少一个
  *:2*i+1的方式递增
  
下三角:先输出空格,后输出*,每行中
  空格:从上往下,每行多一个空格
  *: 从上往下,按照2*(line-1-i)-1的方式减少,其中:line表示总共有多少行
按照上述方式,将上三角和下三角中的空格和*分别输出即可。 
*/
 
 
int main()
{
	int line = 0;
	int i = 0;
	scanf("%d", &line);//7
	//打印上半部分
	for(i=0; i<line; i++)
	{
		//打印一行
		//打印空格
		int j = 0;
		for(j=0; j<line-1-i; j++)
		{
			printf(" ");
		}
		//打印*
		for(j=0; j<2*i+1; j++)
		{
			printf("*");
		}
		printf("\n");
	}
 
 
	//打印下半部分
	for(i=0; i<line-1; i++)
	{
		//打印一行
		int j = 0;
		for(j=0; j<=i; j++)
		{
			printf(" ");
		}
		for(j=0; j<2*(line-1-i)-1; j++)
		{
			printf("*");
		}
		printf("\n");
	}
	return 0;
}
void print(int n)
{
	int i = 0;
	//上半部分
	for (i = 0; i < n; i++)
	{
		//打印1行
		//打印空格
		int j = 0;
		for (j = 0; j < n - 1-i; j++)
		{
			printf("  ");
		}

		//打印*
		for (j = 0; j < (2 * i + 1); j++)
		{
			printf("* ");
		}
		printf("\n");
		
	}

	//下半部分
	for (i = 0; i < n - 1; i++)
	{
		//打印1行
		//打印空格
		int j = 0;
		for (j = 0; j < i + 1; j++)
		{
			printf("  ");
		}

		//打印*
		for (j = 0; j < (n-1) * 2-1-2*i; j++)
		{
			printf("* ");
		}
		printf("\n");
	}
}

题35:喝汽水问题

喝汽水,1瓶汽水1元,2个空瓶可以换一瓶汽水,给20元,可以多少汽水(编程实现)。

/*
思路:
1. 20元首先可以喝20瓶,此时手中有20个空瓶子
2. 两个空瓶子可以喝一瓶,喝完之后,空瓶子剩余:empty/2(两个空瓶子换的喝完后产生的瓶子) + empty%2(不够换的瓶子)
3. 如果瓶子个数超过1个,可以继续换,即重复2
*/
#include <stdio.h>
int main()
{
	int money = 0;
	scanf("%d",&money);//20
	int sumCount = money;//20
	int empty = money;

	while (empty>=2)
	{
		sumCount += empty / 2;
		empty = empty / 2 + empty % 2;;		
	}
	printf("%d\n",sumCount);
	
	return 0;
}
#include <stdio.h>
int main()
{
	int n = 20;
	scanf("%d",&n);

	int sum = n;
	int empty = n;

	while (empty>=2)
	{
		empty -= 2;//两个空瓶换一瓶汽水
		sum += 1;//多喝一瓶汽水
		empty += 1;//增加一个空瓶
	}

	printf("%d\n",sum);
	return 0;
}
int main()
{
	int money = 0;
	scanf("%d",&money);

	int sum = 0;
	int empty = 0;

	while ( (empty >=2) ||money >0 )
	{
		//有钱,就花一元买一瓶汽水
		if (money > 0)
		{
			sum += 1;//喝一瓶汽水
			money -= 1;//少一块钱
			empty += 1;//多一个空瓶
		}

		if (empty >= 2)
		{
			empty -= 2;//少两个空瓶
			sum += 1;//多喝一瓶汽水
			empty += 1;//多一个空瓶
		}
	}
	printf("%d\n",sum);
	return 0;
}

题36:程序死循环解释

VS开发环境调试下面的代码,画图解释下面代码的问题

#include <stdio.h>
int main()
{
    int i = 0;
    int arr[] = {1,2,3,4,5,6,7,8,9,10};
    for(i=0; i<=12; i++)
    {
        arr[i] = 0;
        printf("hello bit\n");
    }
    return 0;
}

调试代码:

i从0变化到11,arr[0]~arr[11]都被赋值为0
在这里插入图片描述
当 i= 12时,F5运行到下一个断点处,i从12变成0了
在这里插入图片描述
因此,程序进入死循环。

首先,我们需要注意以下几点:

1.局部变量在栈区分配空间;

2.栈的生长方向是从高地址向低地址(先使用高地址空间,再使用低地址空间);

3.数组元素的地址随着下标的增大从低到高变化;

这里,先定义了i变量,然后又定义了整型数组arr,所以&i> &arr[9],至于i和arr[9]之间相差多少个地址,我们并不清楚,然后通过调试发现,&i = &arr[12],

所以,在VS2019编译器下,&i和&arr[9]的差值是2(相差两个整型变量,8个字节),所以arr[12] = 0,导致i=0;所以程序进入死循环。

在gcc编译器下,&i和arr[9]之间的地址个数又有所不同,在此我们不做讨论,后续再进行学习。
在这里插入图片描述

题37:调整奇数偶数顺序

调整数组使奇数全部都位于偶数前面。

输入一个整数数组,实现一个函数,

来调整该数组中数字的顺序使得数组中所有的奇数位于数组的前半部分,

所有偶数位于数组的后半部分。

方法1:使用冒泡排序

int main()
{
	int arr[] = {12,1,2,4,6,8,1,3,5,6};

	int i = 0;
	int len = sizeof(arr) / sizeof(arr[0]);

	//趟数
	for (i = 0; i < len -1; i++)
	{
		//每趟比较的对数
		int j = 0;
		int flag = 1;
		for (j = 0; j < len - 1 - i; j++)
		{
			if ((0 == arr[j] % 2) && (1 == arr[j + 1] % 2))
			{
				int tmp = arr[j];
				arr[j] = arr[j+1];
				arr[j + 1] = tmp;
				flag = 0;
			}
		}
		if (1 == flag)
		{
			break;
		}
		
	}

	for (i = 0; i < len; i++)
	{
		printf("%d ",arr[i]);
	}
	return 0;
}

方法2:使用冒泡排序效率太低,我们来使用如下方法

/*
思路:
1. 给定两个下标left和right,left放在数组的起始位置,right放在数组中最后一个元素的位置
2. 循环进行以下操作
 a. 如果left和right表示的区间[left, right]有效,进行b,否则结束循环
 b. left从前往后找,找到一个偶数后停止
 c. right从后往前找,找到一个奇数后停止
 d. 如果left和right都找到了对应的数据,则交换,继续a,
*/
void swap_arr(int arr[], int sz)
{
	int left = 0;
	int right = sz-1;
	int tmp = 0;
 
 
	while(left<right)
	{
     // 从前往后,找到一个偶数,找到后停止
		while((left<right)&&(arr[left]%2==1))
		{
			left++;
		}
     
		// 从后往前找,找一个奇数,找到后停止
		while((left<right)&& (arr[right]%2==0))
		{
			right--;
		}
     
     // 如果偶数和奇数都找到,交换这两个数据的位置
     // 然后继续找,直到两个指针相遇
		if(left<right)
		{
			tmp = arr[left];
			arr[left] = arr[right];
			arr[right] = tmp;
		}
	}
}
int main()
{
	int arr[] = { 12,1,2,4,6,8,1,3,5,6 };
	
	
	int len = sizeof(arr) / sizeof(arr[0]);

	int left = 0;
	int right = len - 1;

	while (left <= right)
	{
		//1.找到第一个偶数
		while ((left <= right) && (arr[left] % 2 != 0))
		{
			left++;
		}

		//全是奇数
		if (left > right)
		{
			break;
		}

		//2.找到第一个奇数
		while ((left <= right) && (arr[right] %2 == 0))
		{
			right--;
		}

		//全是偶数
		if (left > right)
		{
			break;
		}


		//3.交换
		int tmp = arr[left];
		arr[left] = arr[right];
		arr[right] = tmp;

	}

	int i = 0;
	for (i = 0; i < len; i++)
	{
		printf("%d ",arr[i]);
	}
	return 0;
}

题38:strlen实现

模拟实现库函数strlen

1.计数

#include <assert.h>
size_t my_strlen(const char* str)
{
	int count = 0;
	assert(str!=NULL);

	while (*str != '\0')
	{
		count++;
		str++;
	}
	return count;
}

int main()
{
	char str[] = "abcdefgh";
	size_t len = my_strlen(str);
	return 0;
}

2.指针-指针

#include <assert.h>
size_t my_strlen(const char* str)
{
	assert(str!=NULL);
	char* ps = str;
	while (*str!='\0')
	{
		str++;
	}
	return str - ps;
}

int main()
{
	char str[] = "abcdefgh";
	size_t len = my_strlen(str);
	return 0;
}

题39:strcpy实现

模拟实现库函数strcpy

#include <stdio.h>
#include <assert.h>
char* my_strcpy(char* dest,const char* src)
{
	assert(src != NULL && dest != NULL);
	
	char* ret = dest;

	while (*src != '\0')
	{
		*dest = *src;
		dest++;
		src++;
	}
	*dest = *src;
	return ret;
}

int main()
{
	char dest[20] = {0};
	char src[15] = "hello world";
	my_strcpy(dest,src);
	printf("%s\n",dest);
	return 0;
}
char* my_strcpy(char* dest,const char* src)
{
	assert(src != NULL && dest != NULL);
	
	char* ret = dest;

	while (*dest++ = *src++)
	{
		;
	}
	return ret;
}

题40获得月份天数

获得某年某月有多少天,请帮他编程实现。输入年份和月份,计算这一年这个月有多少天。

int main()
{
	int arr[] = {0,31,28,31,30,31,30,31,31,30,31,30,31};

	int year = 0;
	int month = 0;

	while (scanf("%d %d",&year,&month)!=EOF)
	{
		if (month != 2)
		{
			printf("%d\n", arr[month]);
		}
		else
		{
			//判断闰年 
			if ((0 == year % 4) & (year % 100 != 0) || (year % 400 == 0))
			{
				printf("%d\n", 29);
			}
			else
			{
				printf("%d\n", 28);
			}
		}
	}
	return 0;
}

以下方法更好:

#include <stdio.h>
int main()
{
    int y = 0;
    int m = 0;
    int days[12] = {31,28,31,30,31,30,31,31,30,31,30,31}; 
    while(scanf("%d%d", &y, &m) != EOF)
    {
        int day = days[m-1];
        if((y%4==0 && y%100!=0) || (y%400==0))
        {
            if(m == 2)
                day += 1;
        }
        printf("%d\n", day);
    }
    return 0;
}

题41 :杨辉三角

在屏幕上打印杨辉三角。

1
1 1
1 2 1
1 3 3 1
……

使用递归

int Fac(int row,int col)
{
	//使用递归
	if ((0 == col) || (row == col))
	{
		return 1;
	}
	else
	{
		return Fac(row - 1, col - 1) + Fac(row - 1, col);
	}
}

int main()
{
	int n = 0;
	scanf("%d",&n);

	int i = 0;
	for (i = 0; i < n; i++)
	{
		int j = 0;
		for (j = 0; j <= i; j++)
		{
			int ret = Fac(i,j);
			printf("%d ",ret);
		}
		printf("\n");
	}
	return 0;
}

使用循环

由于此题要打印整个杨辉三角的数据而非取出某一项,所以不可避免的一定是要填出每一项,没有偷懒的余地,那就老老实实的根据规律填空即可。按照题设的场景,能发现数字规律为:d[i][j] = d[i - 1][j] + d[i - 1][j - 1]。所以我们只要按照这个方法填表即可。

void yangHuiTriangle(int n)
{
	int data[30][30] = { 1 }; //第一行直接填好,播下种子
 
	int i, j;
 
	for (i = 1; i < n; i++) //从第二行开始填
	{
		data[i][0] = 1; //每行的第一列都没有区别,直接给1,保证不会越界。
		for (j = 1; j <= i; j++) //从第二列开始填
		{
			data[i][j] = data[i - 1][j] + data[i - 1][j - 1]; //递推方程
		}
	}
 
	for (i = 0; i < n; i++) //填完打印
	{
		for (j = 0; j <= i; j++)
		{
			printf("%d ", data[i][j]);
		}
		putchar('\n');
	}
}
 

改进:
由于我在填第n行的杨辉三角时,只跟第n-1行的杨辉三角产生联系,不会跟之前的有联系,所以没必要保存每一行的杨辉三角,填一行打一行就行了,这样能让空间复杂度从n^2降低到n。但是在填数据的时候不能对之前的数据覆盖,所以需要从后向前填。而填杨辉三角顺序对结果是没有影响的,所以可以实现。

void yangHuiTriangle(int n)
{
	int data[30] = { 1 };
 
	int i, j;
	printf("1\n"); //第一行就直接打印了
	for (i = 1; i < n; i++) //从第二行开始
	{
		for (j = i; j > 0; j--) //从后向前填,避免上一行的数据在使用前就被覆盖
		{
			data[j] += data[j - 1]; //公式同上,由于变成了一维,公式也变简单了。
		}
     
		for (j = 0; j <= i; j++) //这一行填完就直接打印了。
		{
			printf("%d ", data[j]);
		}
		putchar('\n');
	}
}

※这种方法虽然降低了空间复杂度,但只能保存最后一行的数据,不利于反复查询,两个填法各有各的适用场景。就本题而言,改进后的胜出。

题42:猜凶手

日本某地发生了一件谋杀案,警察通过排查确定杀人凶手必为4个嫌疑犯的一个。

以下为4个嫌疑犯的供词:
A说:不是我。

B说:是C。

C说:是D。

D说:C在胡说

已知3个人说了真话,1个人说的是假话。

现在请根据这些信息,写一个程序来确定到底谁是凶手。

分别假设凶手为A,B,C,D,然后判断是否有三个人说了真话,一个人说了假话。

int main()
{
	char murder = 0;

	for (murder = 'A'; murder <= 'D'; murder++)
	{
		if ((murder != 'A') + (murder == 'C') + (murder == 'D') + (murder != 'D') == 3)
		{
			printf("%c\n",murder);
			break;
		}
	}
	return 0;
}

C99规定:对于比较运算符(!=,==)的结果如果为真,那么表达式的值为1,否则表达式的值为0。

题43:猜名次

5位运动员参加了10米台跳水比赛,有人让他们预测比赛结果:

A选手说:B第二,我第三;

B选手说:我第二,E第四;

C选手说:我第一,D第二;

D选手说:C最后,我第三;

E选手说:我第四,A第一;

比赛结束后,每位选手都说对了一半,请编程确定比赛的名次。

int main()
{
	int a = 0;
	int b = 0;
	int c = 0;
	int d = 0;
	int e = 0;

	for (a = 1; a <= 5; a++)
	{
		for (b = 1; b <= 5; b++)
		{
			for (c = 1; c <= 5; c++)
			{
				for (d = 1; d <= 5; d++)
				{
					for (e = 1; e <= 5; e++)
					{
						if (1 == (2 == b) + (3 == a) &&
							1 == (2 == b) + (4 == e) &&
							1 == (1 == c) + (2 == d) &&
							1 == (5 == c) + (3 == d) &&
							1 == (4 == e) + (1 == a) &&
							(120 == a * b * c * d * e))
						{
							printf("a = %d, b = %d, c = %d, d = %d,e = %d\n",a,b,c,d,e);
							return 0;
						}
					}
				}
			}
		}
	}
	return 0;
}

考虑到一共五个人,直接模拟推理有些太难,计算机最擅长的遍历此时就会派上用场,将每个人从第1到第5来一遍,则一共会产生5^5种可能性,这个只需要一个5层循环即可搞定。但是这样会导致一些不期望出现的结果出现,因为我并没有查重,所以会出现两个人抢名次的情况,也就是两个人或者更多的人名次相同的情况,例如两个第二,三个第三这样的,所以即使满足了条件,也要查看一下五个人的名次是否重复,这个交给一个函数来执行,只要五个人名次并列,那就返回0,否则返回1即可。有了这个思路,就能完成以下代码。

#include <stdio.h>
 
int checkData(int *p)
{
	int tmp[7] = { 0 }; //标记表,实际是哈希表的思路。一开始每个元素都是0。
 
	int i;
	for (i = 0; i < 5; i++)
	{
		if (tmp[p[i]]) //如果这个位置的标记已经是1,则代表重复,直接返回0。
		{
			return 0;
		}
		tmp[p[i]] = 1; //如果不是,则给这个位置标记为1。
	}
	return 1; //全部标记完毕也没有出现重复的情况,代表OK。
}
 
int main()
{
	int p[5]; //0 1 2 3 4分别代表a b c d e
 
	for (p[0] = 1; p[0] <= 5; p[0]++)
	{
		for (p[1] = 1; p[1] <= 5; p[1]++)
		{
			for (p[2] = 1; p[2] <= 5; p[2]++)
			{
				for (p[3] = 1; p[3] <= 5; p[3]++)
				{
					for (p[4] = 1; p[4] <= 5; p[4]++) //五层循环遍历
					{
                    //这里是五个人的描述,由于比较表达式只有0和1两个结果,如果要两个条件有且只有一个为真,则可以用比较表达式的值总和为1的方式直接判定。别忘了还要判定不能并列。
						if ((p[1] == 2) + (p[0] == 3) == 1 && //B第二,我第三
							(p[1] == 2) + (p[4] == 4) == 1 && //我第二,E第四
							(p[2] == 1) + (p[3] == 2) == 1 && //我第一,D第二
							(p[2] == 5) + (p[3] == 3) == 1 && //C最后,我第三
							(p[4] == 4) + (p[0] == 1) == 1 && //我第四,A第一
							checkData(p) //不能并列
							)
						{
							for (int i = 0; i < 5; i++)
							{
								printf("%d ", p[i]);
							}
							putchar('\n');
						}
					}
				}
			}
		}
	}
 
	return 0;
}

※之后为扩展了解内容。
改进一:
检查是否重复的过程,我们是用一个数组来做的,实际每个标签只有0和1两种可能,没必要一定要用数组做,可以考虑用一个位来做(哈希中的位图),代码如下:

int checkData(int *p)
{
	char tmp = 0;
 
	int i;
	for (i = 0; i < 5; i++)
	{
		tmp |= 1 << p[i]; 
        //tmp每次或上一位1,p[i]如果是1~5都有,则1<<1到1<<5都或上的结果将会是00111110,如果有并列,则一定会至少却其中一个1,结果就不会是00111110,所以可以判断tmp最终的结果是不是这个数字来判断有没有重复。
	}
	return tmp == 0x3E;
}

改进二:
循环代码又长又难看,可以考虑改成递归:

void diveRank(int * p, int n)
{
	if(n >= 5) //此时的n是用来控制循环层数的。
	{
		if ((p[1] == 2) + (p[0] == 3) == 1 && //B第二,我第三
			(p[1] == 2) + (p[4] == 4) == 1 && //我第二,E第四
			(p[2] == 1) + (p[3] == 2) == 1 && //我第一,D第二
			(p[2] == 5) + (p[3] == 3) == 1 && //C最后,我第三
			(p[4] == 4) + (p[0] == 1) == 1 && //我第四,A第一
			checkData(p)) //查重
		{
			for (int i = 0; i < 5; i++)
			{
				printf("%d ", p[i]);
			}
			putchar('\n');
		}
		return;
	}
 
	for(p[n] = 1; p[n] <= 5; p[n]++)
	{
		diveRank(p, n + 1); //通过递归模拟多层循环,每进一次递归相当于进了一层新的循环。
	}
}
 
int main()
{
	int p[5];
 
	diveRank(p, 0);
 
	return 0;
}

改进三:

以上的方法只是让代码简单了点,但还是需要5^5次比较,而如果本来就是做1-5的排列组合的话只需要5!次比较,能极大的减少遍历所需的次数(复杂度由O(n^n)降低为O(n!)),那是不是可以用一个递归完成对1~5的全排列呢?当然是可以的,所以我们可以进一步优化遍历的方式,将遍历用的递归程序改成这样:

#include <stdio.h>
 
void swapArgs(int * a, int * b) //交换函数
{
	int tmp;
	
	tmp = *a;
	*a = *b;
	*b = tmp;
}
 
void diveRank(int * p, int n)
{
	if(n >= 5) //此时的n也是用来控制循环层数的。
	{
		if ((p[1] == 2) + (p[0] == 3) == 1 && //B第二,我第三
			(p[1] == 2) + (p[4] == 4) == 1 && //我第二,E第四
			(p[2] == 1) + (p[3] == 2) == 1 && //我第一,D第二
			(p[2] == 5) + (p[3] == 3) == 1 && //C最后,我第三
			(p[4] == 4) + (p[0] == 1) == 1)   //我第四,A第一
            //由于此时是执行的全排列,所以查重也省了。
		{
			for (int i = 0; i < 5; i++)
			{
				printf("%d ", p[i]);
			}
			putchar('\n');
		}
		return;
	}
 
    int i;
	for(i = n; i < 5; i++) //这个递归方式就完成了对1~5的全排列,方法是从后向前不停的执行交换。可以参考改进二和原代码,将这个递归程序写回成循环后,可以更好的理解。
	{
		swapArgs(p + i, p + n);
		diveRank(p, n + 1);
		swapArgs(p + i, p + n);
	}
}
 
int main()
{
	int p[5] = { 1, 2, 3, 4, 5 }; //当然由于是全排列,所以初值必须给好。
 
	diveRank(p, 0);
 
	return 0;
}

至此,遍历速度上达到了一个新的高度,这种遍历大大减少了遍历的次数,极大的提升了效率。

题44:字符串左旋

实现一个函数,可以左旋字符串中的k个字符。
例如:
ABCD左旋一个字符得到BCDA

ABCD左旋两个字符得到CDAB

方法1:暴力求解

#include <stdio.h>
#include <string.h>
void revolve_string(char* str,int num)
{
	//判断指针的有效性
	assert(str);
	//A  (BCDEF)  BCDEFA
	//B  (CDEFA)  CDEFAB
	int len = strlen(str);
	char c;
	while (num > 0)
	{
		//第一个字符放在c中
		c = str[0];
		//后面所有字符前移
		int i = 0;
		for (i = 0; i < len -1; i++)
		{
			*(str + i) = *(str + i +1);
		}
        //将第一个字符放到最后
		str[len - 1] = c;
		num--;
	}
}
int main()
{
	char arr[] = "ABCDEF";
	int num = 0;
	scanf("%d",&num);
	revolve_string(arr,num);
	printf("%s\n",arr);
	return 0;
}
void revolve_string(char* str,int n)
{
	int len = strlen(str);
	int i = 0;
	for (i = 0; i < n; i++)
	{
		char tmp = str[0];
		int j = 0;
		for (j = 0; j < len -1; j++)
		{
			str[j] = str[j + 1];
		}
		str[len - 1] = tmp;
	}
}
int main()
{
	char arr[] = "ABCDEF";
	int num = 0;
	scanf("%d", &num);
	revolve_string(arr, num);
	printf("%s\n", arr);
	return 0;
}

方法2:
我们可以选择拼接法,一次到位:

void leftRound(char * src, int time)
{
	int len = strlen(src);
	int pos = time % len; //断开位置的下标
	//申一块空间存放
	char tmp[256] = { 0 }; //更准确的话可以选择malloc len + 1个字节的空间来做这个tmp
	
	strcpy(tmp, src + pos); //先将后面的全部拷过来
	strncat(tmp, src, pos); //然后将前面几个接上
	strcpy(src, tmp); //最后拷回去
}

方法3:

先将要左旋的前三个家伙逆序(CBADEFG),然后将后半段也逆序(CBAGFED),最后整体逆序(DEFGABC)即可。这样只需要做数值交换即可,可以写一个函数帮我们完成局部逆序,代码如下:

void reverse_part(char *str, int start, int end) //将字符串从start到end这一段逆序
{
	assert(str);
	int i, j;
	char tmp;
 
	for (i = start, j = end; i < j; i++, j--)
	{
		tmp = str[i];
		str[i] = str[j];
		str[j] = tmp;
	}
}
 
void leftRound(char * src, int time)
{
	assert(src);
	int len = strlen(src);
	int pos = time % len;
	reverse_part(src, 0, pos - 1); //逆序前段
	reverse_part(src, pos, len - 1); //逆序后段
	reverse_part(src, 0, len - 1); //整体逆序
}

方法2和方法3考虑了旋转的个数大于字符串长度的情况,并且效率更高。

题45:字符串旋转结果

写一个函数,判断一个字符串是否为另外一个字符串旋转之后的字符串。

例如:给定s1 =AABCD和s2 = BCDAA,返回1

给定s1=abcd和s2=ACBD,返回0.

AABCD左旋一个字符得到ABCDA

AABCD左旋两个字符得到BCDAA

AABCD右旋一个字符得到DAABC

思路:
本题当然可以将所有旋转后的结果放到一个数组里,然后进行查找,但是这种做法既不好操作,也太费事,但是这题有一个很简单的做法:

其实ABCDE无论怎么旋,旋转后的所有结果,都包含在了ABCDEABCD这个字符串里了。
所以做法很简单,只需要将原字符串再来一遍接在后面,然后找一找待查找的字符串是不是两倍原字符串的子集即可。

方法1:暴力求解

把字符串的所有旋转情况都列出,然后每种情况都与待判断字符串比较

写法1:

#include <string.h>
int is_substr(char* pstr, char* psubstr)
{
	if (strstr(pstr, psubstr))
	{
		return 1;
	}
	else
	{
		return 0;
	}
}

int main()
{
	char arr1[] = "ABCDEFGH";
	char dest[100] = {0};
	strcpy(dest, arr1);
	strcat(dest, arr1);
	char arr2[] = "BCDEFGAH";

	int ret = is_substr(dest,arr2);
	printf("%d\n",ret);
	return 0;
}

写法2:

int findRound(const char * src, char * find)
{
	char tmp[256] = { 0 }; //用一个辅助空间将原字符串做成两倍原字符串
	strcpy(tmp, src); //先拷贝一遍
	strcat(tmp, src); //再连接一遍
	return strstr(tmp, find) != NULL; //看看找不找得到
}

题46:qsort使用练习

练习使用库函数,qsort排序各种类型的数据

#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
struct Stu
{
	char name[20];
	int age;
};

int int_cmp(const void* e1,const void* e2)
{
	return (*(int*)e1 - *(int*)e2);
}

int cmp_by_name(const void* e1,const void* e2)
{
	return strcmp(((struct Stu*)e1)->name,((struct Stu*)e2)->name);
}

int cmp_by_age(const void* e1,const void* e2)
{
	return (((struct Stu*)e1)->age - ((struct Stu*)e2)->age);
}

int float_cmp(const void* e1,const void* e2)
{
	return (*(float*)e1 - *(float*)e2) > 0.0;
}

int main()
{
	int arr[10] = {2,1,4,3,6,5,8,7,10,9};
	
	struct Stu s[3] = { {"peter",25},{"lily",18},{"jack",20}};

	float fArr[8] = {2.5, 1.5, 3.0, 4.5, 6.5, 5.0, 8.5, 7.5};

	qsort(arr,sizeof(arr) / sizeof(arr[0]),sizeof(arr[0]),int_cmp);

	int i = 0;
	for (i =0; i < 10; i++)
	{
		printf("%d ",arr[i]);
	}
	printf("\n");

	qsort(s,sizeof(s)/sizeof(s[0]),sizeof(s[0]),cmp_by_age);

	for (i = 0; i < 3; i++)
	{
		printf("%d ",s[i].age);
	}
	printf("\n");

	qsort(s,sizeof(s) / sizeof(s[0]),sizeof(s[0]),cmp_by_name);


	for (i = 0; i < 3; i++)
	{
		printf("%s ",s[i].name);
	}
	printf("\n");

	qsort(fArr,sizeof(fArr)/sizeof(fArr[0]),sizeof(fArr[0]),float_cmp);

	for (i = 0; i < 7; i++)
	{
		printf("%f ", fArr[i]);
	}
	return 0;
}

题47:杨氏矩阵

有一个数字矩阵,矩阵的每行从左到右是递增的,矩阵从上到下是递增的,请编写程序在这样的矩阵中查找某个数字是否存在。
要求:时间复杂度小于O(N);

#include <stdio.h>
int is_exist_element(int arr[][4],int row,int col,int k)
{
	int x = row - 1;
	int y = 0;

	while ((x >= 0) && (y < col))
	{
		if (arr[x][y] == k)
		{
			return 1;
		}
		else if (arr[x][y] > k)
		{
			x--;
		}
		else
		{
			y++;
		}
	}
}

int main()
{
	int arr[][4] = { {1,2,3,4},{5,6,7,8},{9,10,11,12},{13,14,15,16} };
	int row = sizeof(arr) / sizeof(arr[0]);
	int col = sizeof(arr[0]) / sizeof(arr[0][0]);

	int k = 0;
	printf("请输入要查找的数:>");
	scanf("%d",&k);

	int ret = is_exist_element(arr,row,col,k);
	if (1 == ret)
	{
		printf("存在\n");
	}
	else
	{
		printf("不存在\n");
	}
	return 0;
}

解析:由于杨氏矩阵的特点决定了针对表中的任一元素,下方和右方的数据一定大于我,左方和上方的数据一定小于我,所以查找的时候可以利用这一特点,从左上或者右下角来找。因为这两个位置的大于小于是有区分度的。例如我选择从左上角找,那么没有上边和右边,所以下边一定比我大,左边一定比我小,那么要查找的数字如果比我大,那我就向下,如果比我小,那我就向左,这样查找的次数只有x+y-1次,符合题目中要求的O(n)。

#include <stdio.h>
 
int findnum(int a[][3], int x, int y, int f) //第一个参数的类型需要调整
{
	int i = 0, j = x - 1; //从左上角开始遍历
 
	while (j >= 0 && i < y)
	{
		if (a[i][j] < f) //比我大就向下
		{
			i++;
		}
		else if (a[i][j] > f) //比我小就向左
		{
			j--;
		}
		else
		{
			return 1;
		}
	}
	return 0;
}
 
int main()
{
	int a[][3] = { {1, 3, 5},
				  {3, 5, 7}, 
				  {5, 7, 9} }; //一个示例
 
	if (findnum(a, 3, 3, 2))
	{
		printf("It has been found!\n");
	}
	else
	{
		printf("It hasn't been found!\n");
	}
 
	return 0;
}

若想返回元素的下标,怎么办?在函数内部通过指针来将下标返回。

题48:qsort模拟实现

模仿qsort的功能实现一个通用的冒泡排序

#include <stdio.h
#include <string.h>
int int_cmp(const void* e1,const void* e2)
{
	return *((int*)e1) - *((int*)e2);
}
void swap(void* e1,void* e2,size_t width)
{
	unsigned int i = 0;
	for (i = 0; i < width; i++)
	{
		char tmp = *((char*)e1 + i);
		*((char*)e1+i) = *((char*)e2+i);
		*((char*)e2+i) = tmp;
	}
}

void MyBubbleQsort(void* base,size_t num,size_t width,int(*cmp)(const void* e1,const void* e2))
{
	//比较的趟数
	unsigned int i = 0;
	for (i = 0; i < num - 1; i++)
	{
		int flag = 0;
		//每趟比较的对数
		unsigned int j = 0;
		for (j = 0; j < num - i - 1; j++)
		{
			if (cmp((char*)base + j * width, (char*)base + (j + 1) * width) > 0)
			{
				swap((char*)base + j * width, (char*)base + (j + 1) * width,width);
				flag = 1;
			}
		}
		if (0 == flag)
		{
			break;
		}
	}
}

struct Stu
{
	char name[20];
	int age;
};

int cmp_by_name(const void* e1,const void* e2)
{
	return strcmp(((struct Stu*)e1)->name,((struct Stu*)e2)->name);
}

int main()
{
	int arr[] = {10,9,8,7,6,1,2,3,4,5};
	int sz = sizeof(arr) / sizeof(arr[0]);

	MyBubbleQsort(arr,sz,sizeof(arr[0]),int_cmp);

	int i = 0;
	for (i = 0; i < sz; i++)
	{
		printf("%d ",arr[i]);
	}
	printf("\n");

	struct Stu s[] = { {"zhangsan",25},{"lisi",18},{"wangwu",20}};

	MyBubbleQsort(s,sizeof(s)/sizeof(s[0]),sizeof(s[0]),cmp_by_name);

	for (i = 0; i < 3; i++)
	{
		printf("%s ", s[i].name);
	}
	return 0;
}
  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值