题目大解析

在这里插入图片描述

前言

此文章为题目合集,内含有我对题目的见解和分析,会不时的更新,若对相关题目感兴趣,可以来看看。

题目栏

scanf吃空白情况

在这里插入图片描述
如上图,因为在scanf里面多加了一个空格导致了什么问题呢?按理来说,在输入完1 2 后再按回车就该输出1 2,但是实际上,按完回车后,程序并没有结束,scanf仍然在等待输入,这是因为scanf后面加的空格会进行一个吃空白的操作。

吃空白:也就是把空格、回车、制表符全都吃掉,scanf()默认把空白作为分割多个输入的标志,所以当你输入空白的时候,scanf()会认为你将要进行下一个输入,从而停下来等你输入下一个非空白数据。

也就是说,在你没有输入一个有效字符的时候,你无论输入多少个回车,都会被“吃掉”

关于EOF

在这里插入图片描述

int main() 
{
    int a = 0;
    int b = 0;
   
while (scanf("%d %d", &a, &b)!=EOF)
    {
        
        if (a == b)
            printf("%d%s%d\n", a, "=", b);
        else if (a < b)
            printf("%d%s%d\n", a, "<", b);
        else
            printf("%d%s%d\n", a, ">", b);
   } 
    return 0;
}

题目的思路其实简单,用一个while循环搭配if语句其实就可以求的答案,但是,我要着重讲一下EOF,在之前有个错误版本代码。
在这里插入图片描述
在这里,我的while判断语句是a != EOF && b != EOF,为什么这是错的呢?

错误原因:不能判断a,b是否为EOF,是因为EOF本来就是针对输入结束这样一个结果的返回值,scanf里面的值如果无效(个数/类型啥的出问题)就使得整个scanf无效,这种无效会返回EOF,而像a,b这种如果只是单纯的类型输入错误为无效值,并不会返回EOF,所以不能用EOF去定义a,b的值是否有效或无效。

所以,当判断语句是a != EOF && b != EOF时,因为根本无法这样定义,那就相当于整个判断语句是无效的,即空白,默认为真,那么,此时循环就根本停不下了,而在oj试题种,无法停下来的循环是不行的,即使你可以达到效果。

小知识点补充:sizeof(char) → 返回char型所占空间:1 (Byte)
sizeof(char*) → 返回char*型指针所占空间:4 (Byte)

小乐乐排电梯

在这里插入图片描述

#include <stdio.h>

int main() {
    //需要scanf输入整数,需要if分情况,小于12人,大于12人,如果n%12没有余数情况,时间为
    //n/12*4+2,如果有余数,时间为n/12+2
    int n=0;
    scanf("%d",&n);
    if((n%12)==0)
    printf("%d\n",n/12*4+2);
    else
    printf("%d\n",n/12*4+2);
    return 0;
}

其实可以更加简单,无需if语句

#include <stdio.h>

int main() {
    //需要scanf输入整数,需要if分情况,小于12人,大于12人,如果n%12没有余数情况,时间为
    //n/12*4+2,如果有余数,时间为n/12+2
    int n=0;
    scanf("%d",&n);
 printf("%d\n",n/12*4+2);
   
    return 0;
}

小乐乐找最大数

法一

在这里插入图片描述

#include <stdio.h>

int main() {
   int a,b,c,d;
   while(scanf("%d %d %d %d",&a,&b,&c,&d)!=EOF)
   {
    if(a>=b&&a>=c&&a>=d)
    printf("%d\n",a);
    else if(b>=a&&b>=c&&b>=d)
    printf("%d\n",b);
      else if(c>=a&&c>=b&&c>=d)
       printf("%d\n",c);
        else if(d>=a&&d>=b&&d>=c)
          printf("%d\n",d);
   }
    return 0;
}

这种解法实在太冗长,while循环其实甚至都不用写,接下来将以三目操作符来简便化去解决这道题

法二:三目操作符的运用

#include <stdio.h>

int main() {
   int a,b,c,d,x,y;
    scanf("%d %d %d %d",&a,&b,&c,&d);
    x=a>b?a:b;
    y=c>d?c:d;
    printf("%d",x>y?x:y);

    return 0;
}

三目操作符的作用在这里体现的淋漓尽致,首先先分别从两组a和b,c和d先决出最大值,而后再通过比较,决出最后的最大值,就像是比赛,4强,两组对决,胜者出,参加决赛定冠军,十分巧妙。
而接下来再介绍一个for循环版本

法三:挑战赛法

#include <stdio.h>
 
int main()
{
    int n = 0,a;
    for(int i = 0;i<4;++i)
    {
        scanf("%d",&a);
        if(a>n)
            n=a;
    }
    printf("%d",n);
    return 0;
}

这种方法也十分巧妙,这就像打擂台赛,如果更强(更大),则继承金腰带,一共打4把比赛,有挑战者不断来挑战,挑战成功书写自己的名字(即数字),最后打印。

小乐乐转换成绩

if解法

在这里插入图片描述

#include <stdio.h>

int main() {
   int n=0;
   scanf("%d",&n);
    if(n>=90&&n<=100)
   printf("A\n");
    else if(n>=80&&n<=89)
   printf("B\n");
    else if(n>=70&&n<=79)
   printf("C\n");
    else if(n>=60&&n<=69)
   printf("D\n");
    else if(n>=0&&n<=59)
   printf("E\n");
   else
  printf("F\n");
    return 0;
}

switch解法

#include <stdio.h>
 
int main()
{
    int score = 0;
    scanf("%d", &score);
    int c = score / 10;
    if (score >= 0)
    {
        switch (c)
          {
              case 9:
              case 10:
                  printf("A\n");
                  break;
              case 8:
                  printf("B\n");
                  break;
              case 7:
                  printf("C\n");
                  break;
              case 6:
                  printf("D\n");
                  break;
              case 5:
              case 4:
              case 3:
              case 2:
              case 1:
              case 0:
                  printf("E\n");
                  break;
              default:
                  printf("F\n");
                  break;
          }
    }
    else
    {
        printf("F\n");
    }
 
    return 0;
}

ASCII码

在这里插入图片描述

#include <stdio.h>

int main() {
    int arr[] = { 73, 32, 99, 97, 110, 32, 100, 111, 32, 105, 116 , 33 };
    int i = 0;
    int ch = 0;
    for (i = 0; i < 12; i++)
    {
        ch = arr[i];
        
        putchar(ch);
    }
    return 0;
}

关于getchar和putchar

getchar: getchar() 用于读取用户从键盘输入的单个字符,它有一个整型的返回值,当发生读取错误的时候,返回整型值-1,当读取正确的时候,它会返回用户从键盘输的第一个字符的ASCII码, 当程 序调用getchar时.运行程序时 就等着用户从按 键输入, 用户输入的字符被 存 放在键盘缓冲区中.直到用户按回车为 止(回 车字符也放在缓冲区 中),当用户键入回车之后,getchar才开始从输入流中每次读入一个字符,输入的字符不只一个的时候,后续的getchar调用不会等待用户按键,而直接读取缓冲区中的字符,直到缓冲区中的字符读完之后,才等待用户按键,getchar函数输入数字也按字符处理,单个的getchar函数输入多于一个字符时,只接收第一个字符
putchar: putchar() 向终端输出一个字符。其格式为putchar(ch),其中ch可以是被单引号(英文状态下)引起来的一个字符,可以是介于0~127之间的一个十进制整型数(包含0和127)(超过127就不是ASCII码了),也可以是事先用char定义好的一个字符型变量 当c为一个被单引号(英文状态下)引起来的字符时,输出该字符(注:该字符也可为转义字符 ), 当c为一个介于0~127(包括0及127)之间的十进制整型数时,它会被视为对应字符的ASCII代码,输出该ASCII代码对应的字符; 当c为一个事先用char定义好的字符型变量时,输出该变量所指向的字符

总而言之:1.getchar只能输入字符类型,但因为返回值是整型,所以接收对象必须是整型,去打印接受对象时,输入数字字符时,返回值是首个数字对应ASCII码值的十进制,输入纯字符时,返回值是也是对应对应ASCII码值的十进制。
2.putchar()则是不管()里面塞的是字符还是整型,是字符时候(切记只能一个字符),那么输出的还是字符,整型时(只能[0 127]),输出的为其对应的ASCII码字符

统计数据正负个数

在这里插入图片描述

#include <stdio.h>

int main() {
   int i=0;
    int n=0;
    int p=0;
    int ne=0;
    for(i=0;i<10;i++)
    {
        scanf("%d",&n);
        if(n>0)
         p++;
         else if(n<0)
         ne++;
         else
         continue;
    }
    printf("positive:%d\n",p);
    printf("negative:%d\n",ne);
    return 0;
}

注意:若for循环内部有scanf函数千万要小心别将i放到scanf里去,这样会导致i的值受到改变
做题感悟:一般题目给出数量条件,比如10个,这种已经给好的数字,一般就是用for循环去解决,因为我们使用for循环,目的就是要限定这个题目到达某个量的时候停止循环,而像需要多组输入的,就要用到while循环的scanf判断语句,但while循环虽然也可以因为scanf无效值而停止输出,但我们并没有抱着说需要它一定要在某个量的时候停止循环,只是单纯希望它可以多组输入。

最高分与最低分之差

在这里插入图片描述

#include <stdio.h>

int main() {
    int num = 0;
    scanf("%d", &num);
    int k = 0;
    int i = 0;
    int n = 0;
    int j = 0;
    int a = 100;
    int arr = 0;
   

    for(k=0;k<num;k++)
    {
        scanf("%d", &arr);
        for (i = 0; i < num; i++)
        {
            if (arr >= n)
                n = arr;//最大值

        }
        for (j = 0; j < num; j++)
        {
            if (arr <= a)
                a = arr;//最小值
        }
        
    }
    printf("%d\n", n - a);

   
    return 0;
}

**不足之处:**for循环嵌套太多,其实只要一个就够。

 for(int i = 0;i<n;i++)
    {
        scanf("%d", &m);
        if(m>max)
        {
            max = m;
        }
        if(m<min)
        {
            min = m;
        }
 
    }//类似这样

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

int main()
{
	int a, b, c;
	scanf("%d %d %d", &a, &b, &c);
	if (a >= b && b >= c)
		printf("%d %d %d",a,b,c);
	 else if (a >= c && c >= b)
		printf("%d %d %d", a, c, b);
	 else if (b >= a && a >= c)
		printf("%d %d %d", b, a, c);
	 else if (b >= c && c >= a)
		printf("%d %d %d", b, c, a);
	 else if (c >= a && a >= b)
		printf("%d %d %d", c, a, b);
	 else if (c >= b && b >= a)
		printf("%d %d %d", c, b, a);
 
 
	return 0;
}

弊端反思:类似这种题目,每次都要书写这么多的printf。所以现在有个总结,就是遇到要求输出一组数据的时候,如果是对大小位置顺序来限定,那么我们的思想就要先把这些位置类名好,而后通过代码书写去选出最终适合待在那个位置的数据,而后只需要书写一个printf就可以将全部数据打印出来了。就像这道题,可以改进为如下代码

#include <stdio.h>
int main()
{
    int a = 2;
    int b = 3;
    int c = 1;
    scanf("%d%d%d",&a, &b,&c);
    if(a<b)
    {
        int tmp = a;
        a = b;
        b = tmp;
    }
    if(a<c)
    {
        int tmp = a;
        a = c;
        c = tmp;
    }
    if(b<c)
    {
        int tmp = b;
        b = c;
        c = tmp;
    }
    printf("a=%d b=%d c=%d\n", a, b, c);
    return 0;
}

此代码所妙之处:这边直接让输入地址a,b,c先排好一个序,这样的一个好处就是减少了可能性,因为如果a.b,c刚刚好满足a>b>c的情况,就直接这么打印,所以a>b>c这个情况就无需分析了,而且巧妙之处更甚是,我们可以在此基础上对于其它的情况进行简单的修改,大大减少了代码量。
如果不这么做,而是给位置全部类名新的名字:那么出现的情况就是,每个输入元素可能会在3个位置出现,而当此元素待在一个位置上时又有两种情况,所以分析一个输入元素就要用6串if语句去书写它的情况,那么3个输入元素则需要18串!由此可见此代码的所妙之处

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

int main()
{
	
	int	i = 100;
	int j = 1;
	int n = 0;
	for (i = 100; i < 200; i++)
	{
		n = 0;
		for (j = 1; j <= i; j++)
		{
			if ((i % j) == 0)
				n++;
		}
		if (n == 2)
			printf("%d ", i);


	}

题悟:该题思路如何寻找?首先呢,一看到100到200,我们就会立马感知,这是一个限定范围条件,从而马上联想到可能要运用for循环,然后素数这个条件,说明是有一定的限定条件才能满足某某情况,于是联想到If语句,从而大概的框架就明白了,需要有for循环,需要有If语句,而本体唯一难点,就是如何运用If句去阐述素数。
小知识点:a*b=i,在a,b中至少存在一个数<=sqrt(i),sqrt为开平方,所以,本道题的循环无需循环那么多,直接循环到<=sqrt(i)就至少可以找到一个因子(或者结果是找不到,因为本来就是素数没有)
注意:sqrt的头文件为<math.h>
再提升效率:因为偶数肯定不是素数,所以每次循环可以从奇数开始循环,然后i+=2,后面循环的都是奇数了

打印1000年到2000年之间的闰年

闰年:公元纪年的年数可以被四整除,即为闰年;被100整除而不能被400整除为平年;被100整除也可被400整除的为闰年.如2000年是闰年,而1900年不是.
//1000年不能被400整除, 所以不是闰年.

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

	return 0;
}

判断闰年

int main()
{
	int n = 0;
	while (1)
	{
		printf("请输入年份:");
		scanf("%d", &n);
		if ((n % 100 == 0 && n % 400 == 0) || (n % 4 == 0 && n % 100 != 0))
		{
			printf("%d是闰年\n", n);
		}
		else
			printf("%d不是闰年\n", n);
	}

	return 0;
}

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

法一:数组与挑战赛结合法

int main()
{
	int a, b;
	scanf("%d %d", &a, &b);
	int i = 0;
	int j = 0;
	//初步思路:分别求出a,b所有因数;而后将这些因数放入数组当中,然后用for循环去判断二者的公约数,再存在一个数组当中,而后用挑战赛法找出最大公约数
	int arr1[100] = { 0 };
	int arr2[100] = { 0 };
	int arr3[100] = { 0 };
	int sz1 = 0;
	int sz2 = 0;
	int sz3 = 0;
	int n = 0;
	int n1 = 0;
	int n2 = 0;
	int max = 0;
	for (i = 1,n1=0; i <= a; i++)
	{
		if ((a % i) == 0)
		{
			n1++;
			arr1[n1 - 1] = i;//a的因数
			sz1++;//因数个数
		}
	}
	//打印一下arr1里面的数字
	for (i = 0; i < sz1; i++)
	{
		printf("%d .", arr1[i]);
	}
	for (i = 1,n2=0; i <= b; i++)
	{
		if ((b % i) == 0)
		{
			n2++;
			arr2[n2 - 1] = i;//b的因数
			sz2++;
		}
	}
	//打印一下arr2里面的数字
	for (i = 0; i < sz2; i++)
	{
		printf("%d *", arr2[i]);
	}
	//for循环去判断二者的公约数
	for (i = 0; i < sz1; i++)
	{
		arr1[i];
		for (j = 0; j < sz2; j++)
		{
			if (arr1[i] == arr2[j])
			{
				n++;
				arr3[n - 1] = arr1[i];
				sz3++;
			}
			
		}
	}
	//打印一下arr3里面的数字
	for(i=0;i<n;i++)
	{
		printf("%d @", arr3[i]);
	}
	//用挑战赛法找出最大公约数
	for (i = 0; i < n; i++)
	{
		if (arr3[i] > max)
			max = arr3[i];
	}
	printf("最终answer:%d\n", max);
	return 0;
}

法二:退一步海阔天空法

易知,求a,b之间的最大公约数,这个最大公约数一定是<=二者之中的最小值的。所以思路是先将最大值%最小值,如果0,则求得最大公约数就是这个最小值,如果不是,则退一步,最小值再自降-1,然后再去被最大值%,循环重复这个,直到0的时候求出最大公约数。代码如下

int main()
{
	int a, b;
	scanf("%d %d", &a, &b);
	int k = a < b ? a : b;//先提取出最小值
	while (1)
	{
		if ((a % k == 0) && (b % k == 0))
			break;//达成目的就跑路
		else
			k--;//还没找到,先退一步海阔天空
	}
	printf("%d", k);//😀找到啦
	return 0;
}

法三:辗转相除法

定义:求两个数的最大公约数,比如a,b,先让a%b,此时让a继承b值,b继承余数值,循环下去,直到a%b==0时,即b此时继承的数值为0时,而此时a的值就是最大公约数。

int main()
{
	int a, b;
	
	scanf("%d %d", &a, &b);
	
	while (b!=0)
	{
		int k = a % b;
		a = b;
		b = k;

	}
	printf("%d\n", a);
	return 0;
}

错题收纳处

switch没加break后果

在这里插入图片描述

补充小知识点switch语句中表达式的类型只能是:整形和枚举类型

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

int main()
{
	float sum1 = 0, sum2 = 0, sum3 = 0;
	float a = 0, b = 0;
	int i = 0;


	for (i = 1; i < 100; i+=2)
	{
		a = (1.0) / i;
		sum1 += a;
	}
	for (i = 2; i < 100; i += 2)
	{
		a = (-1.0) / i;
		sum2 += a;
	}
	sum3 = sum1 + sum2;
	printf("%.2f", sum3);
	return 0;


}

这道题本身其实没有难度,但关键点在于如果求得分数?我们既然本道题要求的是分数,那么,我们首先要先要先创建float或double类型的变量。
在这里插入图片描述

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

int main()
{
	int i = 0;
	int sum = 0;
	for (i = 1; i <= 100; i++)
	{
		//十位判断和个位判断是否为9
		if ((i / 10) == 9 || (i - ((i / 10) * 10)) == 9)
			sum++;
	}
	printf("sum:%d\n", sum);
	return 0;
}

有序序列合并

输入描述:
输入包含三行,
第一行包含两个正整数n, m,用空格分隔。n表示第二行第一个升序序列中数字的个数,m表示第三行第二个升序序列中数字的个数。
第二行包含n个整数,用空格分隔。
第三行包含m个整数,用空格分隔。
输出描述:
输出为一行,输出长度为n+m的升序序列,即长度为n的升序序列和长度为m的升序序列中的元素重新进行升序序列排列合并

法一:

#include <stdio.h>
int main()
{
    int n = 0;
    int m = 0;
    int arr1[1000] = {0};
    int arr2[1000] = {0};
    //输入n和m
    scanf("%d %d",&n,&m);
    int i = 0;
    int j = 0;
    //输入两个升序序列
    for(i=0; i<n; i++)
    {
        scanf("%d",&arr1[i]);
    }
    for(i=0; i<m; i++)
    {
        scanf("%d",&arr2[i]);
    }
    //合并有序序列并输出
    i=0;j=0;
    while(i<n && j<m)
    {
        if(arr1[i] < arr2[j])
        {
            printf("%d ",arr1[i]);
            i++;
        }
        else
        {
            printf("%d ",arr2[j]);
            j++;
        }
    }
     
   //判断尚未遍历完的数组是否需要打印输出
    if(i==n && j<m)
        for(;j<m;j++)
            printf("%d ",arr2[j]);
    else
        for(;i<n;i++)
            printf("%d ",arr1[i]);
    return 0;
}

法二:全部在一个数组里

int main() {
    int n, m;
    int i = 0, j = 0;
    int arr[100] = { 0 };
    int tmp;

    scanf("%d %d", &n, &m);
    for (i = 0; i < (n+m); i++)
    {
        scanf("%d", &arr[i]);//全部存在一个数组里面
        
    }
 
    for (i = 0; i < (n + m -1); i++)
    {
        for (j = 0; j < (n + m - 1 - i); j++)
        {
            if (arr[j] > arr[j + 1])
            {
                tmp = arr[j];//两个变量进行交换的时候,一定要第三方变量来做媒介
                arr[j] = arr[j + 1];
                arr[j + 1] = tmp;
            }
        }
    }
    for (i = 0; i < (n + m); i++)
    {
        printf("%d ", arr[i]);
    }
    return 0;
}

小乐乐走台阶(斐波那契数问题)

小乐乐上课需要走n阶台阶,因为他腿比较长,所以每次可以选择走一阶或者走两阶,那么他一共有多少种走法?

法一:递归法

解析:
目标是想求n级台阶有多少种走法,现在先假设已经走完了n级台阶同时假设存在f(n)种走法可以走完n级台阶,现在退回到走完这n级台阶的上一步,即走完这n级台阶的最后一步,最后一步有两种可能的情况,第一种情况是这一步只走了1级台阶,即完成最后一步之前已经走了n-1级台阶,假设走完n-1级台阶存在f(n-1)种走法;第二种情况是最后一步走了两级台阶,即完成最后一步之前已经走了n-2级台阶,又假设走完n-2级台阶存在f(n-2)种走法,那么理一下思路:完成n级台阶最后一步之前需要走完n-1级台阶或者n-2级台阶,因为n级存在台阶f(n)种走法,n-1级台阶存在f(n-1)种走法,n-2级台阶存在f(n-2)种走法,所以f(n)=f(n-1)+f(n-2);
继续递推,完成n-1级台阶的最后一步时之前肯定走了n-2或n-3级台阶,再继续递推,走完n-2级台阶的最后一步时之前肯定走了n-3或n-4级台阶,一直递推下去,则有:f(n) = f(n-1)+f(n-2) , f(n-1) = f(n-2)+f(n-3) , f(n-2) = f(n-3)+f(n-4) , f(n-3) = f(n-4)+f(n-5) , … , f(4) = f(3)+f(2) , f(3) = f(2)+f(1) 至此,递推结束。
因为我们可以轻易知道f(2)和f(1)的值,所以我们可以一直递归到f(2)+f(1)那里,从而就可以慢慢递推求得后面的次数

int fun(int n)
{
	return n <= 2 ? n : fun(n - 1) + fun(n - 2);//直接使用递归
}
int main()
{
	int  fun(int i);
	int n = 0;
	scanf("%d", &n);
	printf("%d\n", fun(n));
}

法二:递推公式直接用

我们既然知道了f(n)=f(n-1)+f(n-2)这一公式,那么我们可以直接用

int main()
{
	int arr[30] = { 0 };
	int i = 0;
	int n = 0;
	arr[0] = 0;
	arr[1] = 1;
	arr[2] = 2;
	scanf("%d", &n);
	for (i = 3; i <= n; i++)
	{
		arr[i] = arr[i - 1] + arr[i - 2];
	}
	printf("%d\n", arr[n]);

	return 0;

}

变种水仙花数 - Lily Number:

把任意的数字,从中间拆分成两个数字,比如1461 可以拆分成(1和461),(14和61),(146和1),如果所有拆分后的乘积之和等于自身,则是一个Lily Number。

例如:

655 = 6 * 55 + 65 * 5

1461 = 1461 + 1461 + 146*1

求出 5位数中的所有 Lily Number

法一:

int main()
{
	int i = 0;
	int count = 0;
	int arr[100] = { 0 };
	int k = 10000;
	int j = 0;
	int sum = 0;
	int a = 0;
	for (i = 10000; i < 100000; i++)
	{
		sum = 0;
		k = 10000;
		for (j = 0; j < 4; j++)
		{
			int left = i / k;
			int right = i - (left * k);
			sum = sum + left * right;
			
			k = k / 10;
		}
		if (sum == i)
		{
			arr[a] = sum;
			a++;
			count++;
		}
	
	}
	for (i = 0; i < count; i++)
	{
		printf("%d ", arr[i]);
	}
	return 0;
}

法二:

#include<stdio.h>
int main()
{
    int i,j;
    for(i=10000;i<99999;i++)
    {
        int sum=0;
        for(j=10;j<=10000;j=10*j)
        {
            sum+=(i/j)*(i%j);
        }
        if(sum==i)
            printf("%d ",i);
    }
    return 0;
}

三角形判断

针对每组输入数据,输出占一行,如果能构成三角形,等边三角形则输出“Equilateral triangle!”,等腰三角形则输出“Isosceles triangle!”,其余的三角形则输出“Ordinary triangle!”,反之输出“Not a triangle!”。

int main()
{
	int a, b, c;
	while (scanf("%d %d %d", &a, &b, &c) != EOF)
	{
		if (a + b > c && a + c > b && b + c > a)
		{
			if (a == b && b == c)
			{
				printf("Equilateral triangle!\n");
			}
			else	if (a == b || b == c || a == c&&(a == b && b == c)==0)
			{
				printf("Isosceles triangle!\n");
			}
			else
				printf("Ordinary triangle!\n");
		}
		else
			printf("Not a triangle!\n");
	}
	return 0;
}

求字符串的大小和长度

#include <stdio.h>
int main()
{
    char str[] = "hello guy";
    printf("%d %d\n", sizeof(str), strlen(str));
	return 0;
}

该代码的输出是?
"hello guy"中除了字母本身有8个,还有一个空格和\0,所以总共的空间是10*1==10,为10个字节,而字符长度不包括\0,所以字符长度是9.

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

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

void init(int arr[])
{
	int i = 0;
	for (i = 0; i < 10; i++)
	{
		arr[i] = 0;//注意,这里形参能够改变实参的原因是,arr指的就是数组首元素的地址,是传址
	}
}
void Print(int arr[])
{
	int i = 0;
	for (i = 0; i < 10; i++)
	{
		printf("%d ", arr[i]);
	}
	printf("\n");
}
void reverse(int arr[],int sz)
{
	int left = 0;
	int right = sz-1;
	
	while (left < right)
	{
		int tmp = 0;
		tmp = arr[left];
		arr[left] = arr[right];
		arr[right ] = tmp;
		left++;
		right--;
	}
}
int main()
{
	int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
	int sz = sizeof(arr) / sizeof(arr[0]);
	Print(arr);
	reverse(arr,sz);
	 Print(arr);
	init(arr);
	Print(arr);
	

}

冒泡排序

int main()
{
	int arr[100] = { 0 };
	int i = 0;
	int n = 0;
	scanf("%d", &n);//确定要有几个整数

	for (i = 0; i < n; i++)
	{
		scanf("%d", &arr[i]);//先存到数组中
	}
	//开始冒泡排序
	for (i = 0; i < n - 1; i++)
	{
		int j = 0;
		for (j = 0; j < n - i - 1; j++)
		{
			int tmp=0;
			if (arr[j] > arr[j + 1])
			{
				tmp = arr[j];
				arr[j] = arr[j + 1];
				arr[j + 1] = tmp;
			}
		}
	 }
	//开始打印
	for (i = 0; i < n; i++)
	{
		printf("%d ", arr[i]);
	}
	return 0;
}

冒泡排序的整体思路就是:相邻互换
n个元素一共要有n-1个元素要进行循环对比
从左向右对比大小,如果比右边的大,则互换,一直互换到与最后一个元素对比完,这算一次循环
每次循环后,就会有将元素中最大的数放到最后,所以每次循环结束后进入下次循环对比,最后一个元素就不用再比了,所以每次循环的次数-1。

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

思路:我们运用递归思想,就是要将整个过程细化成一小部分,我们就假设n的k次方的函数为fun(n,k),而fun(n,k)分为会等于什么呢,一共有三种情况
第一种,当k>0时,fun(n,k)=n*fun(n,k-1)
第二种k=0时,fun(n,k)=1;
第三种,当k<0时,fun(n,k)=1/fun(n,-k)

所以由这三种情况,我们可以写出递归代码

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

我们记得要用double接收函数返回值,因为K<0时,传回来的值是小数

经过这题递归思想做的题,我对于递归有了一些见解

1.递归的思路无非就是拆,化解成一个小部分分析
而递归法的妙处就是你并不用知道内部函数的具体操作是怎么进行的,一般我们都是通过假想一个函数,然后这个函数已经实现了我们所需要的功能,然后找到函数种第n次与n-1次,甚至n-2次之间的关系,这样说不够全面,准确说,是找到fun(n)与函数fun中的一些参数改变之后存在的某种关系。
如果能发现这个关系,我们就可以将其与fun(n)建立起等式关系,而这样,我们就可以进入递归
2.递归法中一定涉及限制条件
3.每次递归都更接近这个限制条件
4.必定递归到最后有个已知的条件然后往回带
5.函数的作用域只有一个环节
6.只有当fun(n)与fun(n-1)中,实在找不到里面参数的关系时,才要真正编写fun(n)的一些具体操作

计算一个数的每位之和(递归实现)

写一个递归函数DigitSum(n),输入一个非负整数,返回组成它的数字之和
例如,调用DigitSum(1729),则应该返回1+7+2+9,它的和是19
输入:1729,输出:19

我们要做的工作就是将一个非负整数拆成一个个组成它的数字,我们先不要去管这个过程怎么实现
直接设一个函数fun(n),这个函数已经能实现这个功能,我们所作的就是找关系
我们比如fun(1729),它可以直接实现将9脱离出来,而后我们又需要,所以再调用fun(172)得到2,
fun(1729)=fun(172)+9,fun(17)+2,……
以此类推下去,直到fun(1),此时已经没得脱离了,直接输出1就行。
也就是说限制条件就是,当n<10时,不再执行脱离。
而最终我们要的关系结果就是fun(n/10)+n%10

int fun(n)
{
	if (n >= 10)
	{
		return fun(n/10)+n%10;
	}
	else
		return n;
}
int main()
{
	int n = 0;
	scanf("%d", &n);
	int sum = fun(n);
	printf("%d\n", sum);
	return 0;
}

字符串逆序(递归实现)

编写一个函数 reverse_string(char * string)(递归实现)
实现:将参数字符串中的字符反向排列,不是逆序打印。
要求:不能使用C函数库中的字符串操作函数。
比如:
char arr[] = “abcdef”;
逆序之后数组的内容变成:fedcba

递归思路:先设置一个函数f
1.步骤分离
f(abcdef) a f互换
f(bcde) b e互换
f(cd) c d互换
我们只要找到f(abcdef) a f互换与f(bcde) b e互换之间的关系就行

#include <string.h>
void fun(char* str)
{    
	int len = strlen(str);
	if (len >= 2)
	{
		char tmp = *str;
		*str = *(str + len - 1);
		*(str + len - 1) = '\0';
		fun(str + 1);
		*(str + len - 1) = tmp;
	}


}
int main()
{
	char arr[] = "abcdef";
	printf("%c\n", arr[0]);
	fun(arr);
	printf("%s\n", arr);
	return 0;
}

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

//递归和非递归分别实现求n的阶乘(不考虑溢出的问题)
//int Factorial(int n)//递归法
//{
//	if (n > 1)
//		return n * Factorial(n - 1);
//	else
//		return 1;
//}
int fun(n)
{
	int i = 0;
	int sz = n - 1;
	int sum = 1;
	if (n > 1)
	{
		for (i = 0; i < sz; i++)
		{
			sum = sum * n;
			n--;

		}
		return sum;
	}
	else
		return 1;
}
int main()
{
	int n = 0;
	scanf("%d", &n);
	/*int res = Factorial(n);*/
	int res = fun(n);
	printf("%d\n", res);
	return 0;
}

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

思路:
打印1234,打印一回,123 4. 打印两回,12 3 4
所以得出关系,fun(n)=fun(n/10),printf(“n%10”);

void Print(int n)
{
	if (n > 9)
	{
		Print(n / 10);
		printf("%d ", n % 10);
	}
	else
		printf("%d ", n);
}
int main()
{
	int n = 0;
	scanf("%d", &n);
	Print(n);

	return 0;
}

递归和非递归分别实现strlen

//递归和非递归分别实现strlen
int GetLen(char* str)
{
	//非递归
	//int count = 0;
	//while (1)
	//{
	//	if (*str != '\0')
	//	{
	//		count++;
	//		str++;
	//	}
	//	else
	//		break;
	//}
	//return count;
	
	//递归
	if (*str != '\0')
	{
		return(1 + GetLen(str + 1));
	}
	else
		return 0;
}
int main()
{
	char arr[] = "Just do it";
	int ret = GetLen(arr);
	printf("%d\n", ret);
	return 0;
}

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

void Print(char* str)
{
	while (*str != '\0')
	{
		printf("%c", *str);
		str++;
	}
}
int main()
{
	char arr[] = "You are handsome";
	Print(arr);
	return 0;
}

打印菱形

在这里插入图片描述

int main()
{
	int i = 0;
	int j = 0;
	int p = 0;
	int k = 6 ,b=1;
	for (i = 0; i <13; i++)
	{
		if (i <= 6)
		{
			int sz = 1 + 2 * i;
			for (j = k; j > 0; j--)
			{
				printf(" ");
			}
			k--;
			for (j = 0; j < sz; j++)
			{
				printf("*");
			}
			printf("\n");
		}
		else
		{
			int sz = 13 - 2 * (i - 6);
			for (j = 0; j < b; j++)
			{
				printf(" ");
			}
			b++;
			for (p = 0; p < sz; p++)
			{
				printf("*");
			}
			printf("\n");

		}

	}
	return 0;
}

打印水仙花数(全递归实现)

求出0~100000之间的所有“水仙花数”并输出。
“水仙花数”是指一个n位数,其各位数字的n次方之和确好等于该数本身,如:153=1^ 3+5^ 3+3^3,则153是一个“水仙花数”。

#include <math.h>
int GetLen(int n)
{
	if (n > 9)
		return GetLen(n / 10) + 1;
	else
		return 1;
}

int GetSum(int n,int len)
{
	if (n > 9)
		return (GetSum(n / 10, len) + pow(n % 10, len));
	else
		return pow(n, len);
}
int main()
{
	int i = 0;
	for (i = 0; i < 100000; i++)
	{
		int len = GetLen(i);//获得几位数
		int res=GetSum(i,len);//求每一位数的len次方之和
		if (res == i)
			printf("%d ", i);
	}
	return 0;
}

在这里插入图片描述

计算求和(前五项)

求Sn=a+aa+aaa+aaaa+aaaaa的前5项之和,其中a是一个数字,
例如:2+22+222+2222+22222

int GetLen(int n)
{
	if (n > 9)
		return GetLen(n / 10) + 1;
	else
		return 1;
}
int main()
{
	int n = 0;
	int sum = 0;
	int res = 1;
	scanf("%d", &n);
	int add = n;
	int len = GetLen(n);//求出是几位数
	int multiply = pow(10, len);//这是每次所要乘的大小
	int i = 0;
	for (i = 0; i < 5; i++)
	{   
		
		if (i > 0)
		{    
			n=n*multiply+add;//这是下一个数的值,这里设置一个常量add,是因为每次乘法完加的都是这个量,所以给它固定住
			sum =sum+n ;//这里求的是前五项的和
		}
		else
			sum = n;

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

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

int main()
{
	
	int count = 0;
	int money = 0;
	int empty = 0;
	scanf("%d", &money);
	int can_buy = money;
	empty = can_buy;
	while(empty > 1)
	{
		count = count + empty / 2;
		empty = empty / 2 + empty % 2;

	}
	printf("%d\n", count+money);


	return 0;
}

创建一个函数实现strcpy功能

#include <string.h>
void my_strcpy(char* str1, char* str2)
{
	while (*str1 != '\0')
	{
		*str2 = *str1;//其实可以直接优化为*str2++ = *str1++;
		str1++;
		str2++;
	}
	*str2 = *str1;//这里记得还要拷贝\0给其,让其为一个完整的字符串
}
void my_strcpy(char* str1, char* str2)//法二,更简便法
{
	while (*str1++ = *str2++)//这个意思为当值为\0时(\0的ASCII码为0)时为假,循环结束
	{
		;
	}
}
int main()
{
	char arr1[] = "hello world";
	char arr2[20] = { 0 };
	char arr3[20] = { 0 };

	//strcpy(arr2, "hello world");
	//strcpy(arr3, arr1);
	my_strcpy(arr1, arr2);
	printf("%s\n", arr2);
	//printf("%s\n", arr3);

	return 0;
}

strcpy返回的是目标函数的起始位置

断言的使用,设置断言是为了方便程序员,上述代码当中其实存在隐患,假如说指针为空指针,则会出现错误,为了在程序运行起来在展现窗口中可以看到哪里错误,我们可以要提前在关键语句中进行断言
在这里插入图片描述
assert的头文件为<assert.h>

指针修改const修饰的变量?

在这里插入图片描述
修改成功

const修饰指针(const放在*右边)

1.*p不能改动了,即此时p指向的变量值不能改动了
2.但是p可以改,p指向的地址可以改动

const修饰指针(const放在*左边)

1.*p可以改动
2.p不能改动

const多去运用,可以防止未来不小心把不想改的变量给改了。

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

题目:
输入一个整数数组,实现一个函数,
来调整该数组中数字的顺序使得数组中所有的奇数位于数组的前半部分,
所有偶数位于数组的后半部分。

int main()
{
	int arr[10] = { 0 };
	int arr2[10] = { 0 };

	int i = 0;
	int sz = sizeof(arr) / sizeof(arr[0]);
	int j = 0, k = 0;
	for (i = 0; i < sz; i++)
	{
		scanf("%d", &arr[i]);//装入数组当中

	}
	for (i = 0; i < sz; i++)
	{
 		if (arr[i] % 2 == 1)
		{
			j++;
			arr2[j-1] = arr[i];//要创建新数组来存放!
			
		}
	
	}
	for (i = 0; i < sz; i++)
	{
		if (arr[i] % 2 == 0)
		{
			j++;
			arr2[j-1] = arr[i];
			
		}
	}
	for (i = 0; i < sz; i++)
	{
		printf("%d ", arr2[i]);
	}
	/*printf("%d\n", 1 % 2);*/
	return 0;
}

猜名次

5位运动员参加了10米台跳水比赛,有人让他们预测比赛结果:
A选手说:B第二,我第三;
B选手说:我第二,E第四;
C选手说:我第一,D第二;
D选手说:C最后,我第三;
E选手说:我第四,A第一;
比赛结束后,每位选手都说对了一半,请编程确定比赛的名次。

法一:五重循环嵌套法

int check_data(int* p)
{
	int tmp[6] = { 0 };//标记法,记得tmp数组至少要给6,因为p[i]最大为5,所以tmp的下标最大必须至少为5
	int i = 0;
	for (i = 0; i < 5; i++)
	{
		if (tmp[p[i]])//如果名次重复,此时tmp[p[i]]==1,此时就会return0
		{
			return 0;
		}
		else
			tmp[p[i]] = 1;//如果这个名次是第一次出现,则标记为1
	}
	return 1;//全部遍历过去,如果都没有名次重复,则return1
}
int main()
{
	int p[5];
	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]++)
					{
						//因为每位选手都有5种可能,所以一共会产生5*5种可能,所以用5个循环嵌套将25种可能都遍历过去
						if ((p[1] == 2) + (p[0] == 3) == 1 && (p[1] == 2) + (p[4] == 4) == 1 && (p[2] == 1) + (p[3] == 2) == 1 && (p[3] == 3) + (p[2] == 5) == 1 && (p[4] == 4) + (p[0] == 1) == 1 && check_data(p))
						{
							//因为每个人说话有一半是真话,所以真话和假话用真和假表示就是1和0.所以将它们相加就是1,所以如果判断语句全真,即满足条件
							//但是这还不够,可能会出现一些人的名次出现重复,为了防止这种情况,我们要再创建一个check_data函数判断是否名次重复

							//一切都满足后,可以开始打印
							printf("A=%d B=%d C=%d D=%d E=%d\n", p[0], p[1], p[2], p[3], p[4]);
						}

					}
				}
			}
		}

	}
	return 0;
}

总体流程思路:将所有可能都遍历过去,最后根据checkdata函数判断是否符合结果

check_data函数改进

int check_data(int* p)
{
	char tmp = 0;//类型为char因为只需要它的比特位长度为8
	int i = 0;
	for (i = 0; i < 5; i++)
	{
		tmp |= 1 << p[i];
		// p[i]若没有重复名次,5次循环下来,tmp与上一位移位后的1或后,最终结果会是tmp==00111110,16进制表示为0x3E
	}
	return (tmp == 0x3E);//如果为真,返回1
}

找凶手

法一(遍历法)

//日本某地发生了一件谋杀案,警察通过排查确定杀人凶手必为4个嫌疑犯的一个。
//以下为4个嫌疑犯的供词:
//A说:不是我。
//B说:是C。
//C说:是D。
//D说:C在胡说
//已知3个人说了真话,1个人说的是假话。
//现在请根据这些信息,写一个程序来确定到底谁是凶手。

//16种可能性,全部遍历过去,用if语句实现3个人说了真话,1个人说的是假话这句话的正确性,如果正确,打印凶手
//1是凶手,0不是凶手
//int checkdata(int* p)
//{
//	int count = 1;
//	while (*p != '\0')
//	{
//		if (*p == 1)
//			count++;
//		p++;
//	}
//	return (count == 1);//如果凶手满足只有一个,则返回1
//
//}
int main()
{
	int p[4];//p[0]A.p[1]B,p[2]C,p[3]D
	for (p[0] = 0; p[0] < 2; p[0]++)
	{
		for (p[1] = 0; p[1] < 2; p[1]++)
		{
			for (p[2] = 0; p[2] < 2; p[2]++)
			{
				for (p[3] = 0; p[3] < 2; p[3]++)
				{
					if ((p[0] == 0) + (p[2] == 1) + (p[3] == 1) + (p[3] == 0) == 3 && checkdata(p))//检验四话的3真1假,还有凶手的唯一性
					{
						if (p[0])
						{
							printf("A是凶手\n");
							goto end;
						}
						if (p[1])
						{
							printf("B是凶手\n");
							goto end;
						}
						if (p[2])
						{
							printf("C是凶手\n");
							goto end;//找到凶手后就退出循环
						}
						if (p[3])
						{
							printf("D是凶手\n");
							goto end;
						}

					}
				}
			}
		}
	}
	end:
	return 0;
}

法二(假设凶手法)

int main()
{
	int killer = 0;
	//直接假设A,B,C,D中某一个为凶手,循环遍历过去每一个人为凶手时是否满足4话的三真一假
	for (killer = 'a'; killer <= 'd'; killer++)
	{
		if ((killer != 'a') + (killer == 'c') + (killer == 'd') + (killer != 'd') == 3)
			printf("凶手是:%c\n", killer);
	}
	return 0;
}

打印杨辉三角

法一:二维数组

int main()
{
	int n = 0;
	int arr[100][100] = { 0 };//先全部赋为0,这边的0是为了配合1相加得到下一行右侧的1
	int i = 0, j = 0;
	while (n < 1 || n>100)//这个循环说明如果行数非法,则重新输入
	{
		printf("请输入你要打印的杨辉三角行数:");
		scanf("%d", &n);

	}
	//
	for (i = 0; i < n; i++)
		arr[i][0] = 1;//先给每一行的第一个元素赋值为1
	for (i = 1; i < n; i++)//第一行已经赋值1了。所以直接从第二行开始赋值
	{
		for (j = 1; j <= i; j++)//每行要打印多少个元素
		{
			arr[i][j] = arr[i - 1][j] + arr[i - 1][j - 1];//当前行的该元素为两肩上元素之和
			
		}
	}
	//全部成功赋值完之后,就可以开始打印
	for (i = 0; i <n; i++)
	{
		for (j = n; j > i; j--)
		{
			printf("  ");//打印空格
		}
		for (j = 0; j <= i; j++)
		{
			
			printf("%5d", arr[i][j]);
		}
		printf("\n");
	}

	return 0;
}

法二:一维数组

int main()
{
	int n = 0;
	int arr[100] = { 0 };
	int i, j, a, b;
	while (n < 1 || n>100)
	{
		printf("请输入你要打印的杨辉三角行数:");
		scanf("%d", &n);
	}
	for (i = 1; i <= n; i++)//打印n行
	{
		a = 0;//每次进入下一行,都要将a重新赋值为0,打印第一个元素1
		for (j = n; j > i; j--)
					{
						printf("  ");//打印空格
					}
		for (j = 0; j < i; j++)//打印i个元素
		{
			arr[0] = 1;//每一行的一开始首个元素都是为1
			b = arr[j];
			arr[j] = b + a;
			a = b;//a继承该arr[j]的值,为下一行的元素保留两肩元素
			printf("%5d", arr[j]);
			//每行打印完后的循环,都会将i个元素给初始化,作为下一行元素的两肩元素
		}
		printf("\n");
	}
	return 0;
}

字符串左旋

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

int main()
{
	char arr[10] = "ABCDE";
	char* p = arr;
	int k = 0;
	while (1)
	{
	again:
		printf("请输入你想左旋的字符数:");
		scanf("%d", &k);
		printf("左旋前:ABCDE\n");
		if (k < 0 || k>4)
		{
			printf("旋转字符数不合理,重新输入\a");
			goto again;
		}
		int i = 0;
		for (i = 0; i < k; i++)
		{
			arr[5 + i] = *(p + i);
		}
		printf("左旋后:");

		for (i = 0; i < 5; i++, k++)
		{
			printf("%c", *(p + k));//从p+k地址开始打印
		}
		printf("\n");
}
	
}

在这里插入图片描述

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值