学习日记 | C语言经典例题②(实例21-40)

©一颗斯特拉
【注】
1.标有❤️的是值得多做的题目。
2.标有II、III的是二刷、三刷的题目。

  1. 题目来源于C语言经典例题(菜鸟教程100例)

——2.19更新——

实例21:【循环】❤️II

题目:猴子吃桃问题:猴子第一天摘下若干个桃子,当即吃了一半,还不过瘾,又多吃了一个。第二天早上又将剩下的桃子吃掉一半,又多吃了一个。以后每天早上都吃了前一天剩下的一半零一个。到第10天早上想再吃时,见只剩下一个桃子了。求第一天共摘了多少。

01程序分析:
第一次:采用逆向思维,第10天想吃的时候只剩下1个是定量。
02Bad Solution:
03Correct Solution:
【第一种】

#include
int main() {
    int i,x;
    x=1;
    for(i=9;i>=1;i--)
    {
        x=(x+1)*2;
    }
    printf("第一天共摘了多少: %d",x);
    return 0;
}

【第二种】

#include
int main() {
    int day,x1,x2;
    day=9;
    x2=1;
    while(day>0)
    {
        x1=(x2+1)*2;
        x2=x1;
        day--;
    }
    printf("第一天共摘了多少: %d",x1);
    return 0;
}

【运行结果】

第一天共摘了多少: 1534

【第三种】

#include

//函数返回第n天的桃子数
int num(int n){
    if(n==10)
        return 1;
    else
        return (num(n+1)+1)*2;
}

int main() {
    int i;
    printf("第一天共摘了多少: %d",num(1));
    return 0;
}

04题目总结:
暂无

实例22:【乘法表】

使用嵌套 for 循环输出九九乘法口诀表。

【注】详见实例8

实例23:【双重循环➕打印图案】

题目:打印出如下图案(菱形)。
要求

01程序分析:
暂无
02Bad Solution:
暂无
03Correct Solution:

#include 
int main()
{
   int i,j,k;
   for(i=0;i<=3;i++) {//一般从0下标开始
       for(j=0;j<=2-i;j++) {
           printf(" ");
       }
       for(k=0;k<=2*i;k++) {
           printf("*");
       }
       printf("\n");
   }
   for(i=0;i<=2;i++) {
       for(j=0;j<=i;j++) {
           printf(" ");
       }
       for(k=0;k<=4-2*i;k++) {
           printf("*");
       }
       printf("\n");
   }
   return 0;

04题目总结:
暂无


——2.21更新——

实例24:【数列求和】❤️II

题目:有一分数序列:2/1,3/2,5/3,8/5,13/8,21/13...求出这个数列的前20项之和。

01程序分析:
第一次:先考虑如何输出这个数列,可以看到分子是前一项的分子与分母之和,分母是前一项的分子,这样必须用到一个临时存储变量。再考虑前20项怎么抓出来,应该可以用一个计数器。
02Bad Solution:
暂无
03Correct Solution:

#include
int main()
{
    float sum=0,i=1,j=2;
    int count,temp;
    for(count=1;count<=20;count++)
    {
        sum=sum+j/i;
        temp=j;//需要开辟一个临时变量
        j=i+j;
        i=temp;
    }
    printf("前20项的和是: %f ",sum);
}

【运行结果】

前20项的和是: 32.660263

04题目总结:
1.注意除法问题。如果两个变量都是整型,则相除后得到的数也是整形。
2.浮点数

浮点数

实例25:【阶乘求和】❤️

题目:求1+2!+3!+...+20!的和。

01程序分析:
第一次:和上一道题差不多,只是把累加变成了累乘。
02Bad Solution:

#include
int main()
{
    int i,a=1,sum=0;
    for(i=1;i<=20;i++)
    {
        a=i*a;
        sum=a+sum;
    }
    printf("和是: %d ",sum);
}

【运行结果】

和是: 268040729

【错误】
1.结果和每项设置为int类型不能正确的显示结果。超出了范围。
03Correct Solution:

#include
int main()
{
    int i;
    long double a=1,sum=0;
    for(i=1;i<=20;i++)
    {
        a=a*i;
        sum=sum+a;
    }
    printf("和是: %Lf ",sum);
}

【运行结果】

和是: 2561327494111820313.000000

04题目总结:
1.区分三种浮点数:
floatdoublelong double

浮点类型
输出说明:
参数
长度

2.一字节表示八位,即:1byte = 8 bit;

int: 4byte = 32 bit 有符号signed范围:2^31-1 ~ -2^31即:2147483647 ~ -2147483648无符号unsigned范围:2^32-1 ~ 0即:4294967295 ~ 0

long: 4 byte = 32 bit 同int型

double: 8 byte = 64 bit 范围:1.79769e+308 ~ 2.22507e-308

long double: 12 byte = 96 bit 范围: 1.18973e+4932 ~ 3.3621e-4932

float: 4 byte = 32 bit 范围: 3.40282e+038 ~ 1.17549e-038

实例26:【递归】

题目:利用递归方法求5!。

01程序分析:
第一次:要定义一个函数,并且要在这个函数中调用自身。
02Bad Solution:
暂无
03Correct Solution:

#include

double factorial(unsigned int i)
{
    if(i<=1)
    {
        return 1;
    }
    return i*factorial(i-1);
}

int main()
{
    int i=5;
    printf("%d 的阶乘为 %f\n",i,factorial(i));
    return 0;
}

【运行结果】

5 的阶乘为 120.000000

04题目总结:
1.递归

  • 循环是有去无回,而递归则是有去有回(因为存在终止条件)。
  • 递归指的是在函数的定义中使用函数自身的方法。C 语言支持递归,即一个函数可以调用其自身。但在使用递归时,程序员需要注意定义一个从函数退出的条件,否则会进入死循环。
  • 递归函数在解决许多数学问题上起了至关重要的作用,比如计算一个数的阶乘、生成斐波那契数列,等等。
    递归的执行

实例27:【逆序输出】❤️II

题目:利用递归函数调用方式,将所输入的5个字符,以相反顺序打印出来。

01程序分析:
第一次:
02Bad Solution:
暂无
03Correct Solution:

#include 

void palin(int n)
{
    char next;
    if(n<=1) {
        next=getchar();
        printf("相反顺序输出结果\40:\40");
        putchar(next);
    } else {
        next=getchar();
        palin(n-1);//到了递归出口返回时才执行下面的语句
        putchar(next);
    }
}

int main()
{
    int i=5;
    void palin(int n);
    printf("请输入5个字符\40:\40");
    palin(i);
    printf("\n");
}

【运行结果】

请输入5个字符 : abcde
相反顺序输出结果 : edcba

04题目总结:
暂无

实例28:【递推】❤️

题目:有5个人坐在一起,问第五个人多少岁?他说比第4个人大2岁。问第4个人岁数,他说比第3个人大2岁。问第三个人,又说比第2人大两岁。问第2个人,说比第一个人大两岁。最后问第一个人,他说是10岁。请问第五个人多大?

01程序分析:
第一次:用递推的方法,有两个过程:递推和回推。
02Bad Solution:
暂无
03Correct Solution:

#include
int year(unsigned int i)
{
    if(i==1)
    {return 10; }
    return year(i-1)+2;
}
int main()
{
    printf("第5个人 %d 岁",year(5));
    return 0;
}

【运行结果】

第5个人 18 岁

04题目总结:

实例29:【求位数+逆序】❤️

题目:给一个不多于5位的正整数,要求:一、求它是几位数,二、逆序打印出各位数字。

01程序分析:
第一次:递归函数为计位数函数,且可打印。
02Bad Solution:
暂无
03Correct Solution:

#include
int fun(int para)
{
    if(para/10==0){
        printf("%d",para);
        return 1;
    }
    printf("%d",para%10);
    return fun(para/10)+1;//计一位数
}
int main()
{
    int test;
    printf("请输入一个不多于5位的正整数:");
    scanf("%d",&test);
    printf("逆序输出该数:");
    int res=fun(test);
    printf("\n该数是 %d 位数",res);
    return 0;
}

【运行结果】

请输入一个不多于5位的正整数:1212
逆序输出该数:2121
该数是 4 位数

04题目总结:

实例30:【回文数】

题目:一个5位数,判断它是不是回文数。即12321是回文数,个位与万位相同,十位与千位相同。

01程序分析:
第一次:与实例13一样,需要取出每位上的数。只不过,这里是5位数,那里是3位数。
02Bad Solution:
暂无
03Correct Solution:

#include
int main()
{
    int num,n1,n2,n3,n4,n5;
    printf("请输入一个五位数:");
    scanf("%d",&num);
    n5=num/10000;
    n4=num%10000/1000;
    n3=num%1000/100;
    n2=num%100/10;
    n1=num%10;
    if(n1==n5&&n2==n4)
    {
        printf("是回文");
    }
    else{printf("不是回文");}
    return 0;
}

【运行结果】

请输入一个五位数:12321
是回文

04题目总结:
1.取每位上的数的操作可以记下来。先取余后整除。


——2.22更新——

实例31:【switch】❤️

题目:请输入星期几的第一个字母来判断一下是星期几,如果第一个字母一样,则继续判断第二个字母。

01程序分析:
第1次:这个问题,用到判断中的switch语句。需要两次判断的,在对应case里加if条件语句。
02Bad Solution:
暂无
03Correct Solution:

#include
int main() {
    char c,d;
    printf("请输入第1个字母:");
    scanf("%c",&c);
    getchar();
    switch(c) {
        case 'm':
            printf("星期一");
            break;
        case 't':
            printf("请输入第2个字母:");
            scanf("%c", &d);
            if (d == 'u') { printf("星期二"); }
            else { printf("星期四"); }
            break;
        case 'w':
            printf("星期三");
            break;
        case 'f':
            printf("星期五");
            break;
        case 's':
            printf("请输入第2个字母:");
            scanf("%c", &d);
            if (d == 'a') {
                printf("星期六");
            }
            else { printf("星期天"); }
            break;
        default : printf("错误!没有匹配的结果。");break;
    }
    return 0;
}

【运行结果】

请输入第1个字母:s
请输入第2个字母:a
星期六

04题目总结:
1.switch用法总结:

  • 当被测试的变量等于 case 中的常量时,case 后跟的语句将被执行,直到遇到 break 语句为止。
  • 当遇到 break 语句时,switch 终止,控制流将跳转到 switch 语句后的下一行。
  • 不是每一个 case 都需要包含 break。如果 case 语句不包含 break,控制流将会 继续 后续的 case,直到遇到 break 为止。
  • 一个 switch 语句可以有一个可选的 default case,出现在 switch 的结尾。default case 可用于在上面所有 case 都不为真时执行一个任务。default case 中的 break 语句不是必需的。

2.C 库函数 getchar()

int getchar(void)

从标准输入 stdin 获取一个字符(一个无符号字符)。这等同于 getc 带有 stdin 作为参数。

3.scanf()输入后,会在末尾加一个换行符,所以为避免后面的scanf()读取换行符,这里需要加一个getchar()

在用 %c 输入时,空格和"转义字符"均作为有效字符。

====考点1:函数====【起】====

下面几个实例都是有关函数的题目。这里先攻克函数中的一个难点,即函数参数中的传值调用和引用调用。
当时学习的时候,我还是经历了一番波折才弄懂了这两者的差别。明白了这个问题,你对内存地址的理解就很透彻了。
下面我们通过一个例子看一下这两种调用的区别。

题目:编写一个函数,交换两整数的值。

1.传值调用

#include
void swap(int num1,int num2);
int main(){
    int a,b;
    printf("请输入a,b的值:");
    scanf("%d%d",&a,&b);
    printf("交换前,a的值为: %d\n",a);
    printf("交换前,b的值为: %d\n",b);
    swap(a,b);
    printf("交换后,a的值为: %d\n",a);
    printf("交换后,b的值为: %d\n",b);
    return 0;
}

void swap(int num1,int num2){
    int temp;
    temp=num2;
    num2=num1;
    num1=temp;
}

【运行结果】

请输入a,b的值:1 2
交换前,a的值为: 1
交换前,b的值为: 2
交换后,a的值为: 1
交换后,b的值为: 2

【注】我们可以看到,尽管调用了交换函数swap,但是a,b的值并未改变。这是因为传值调用是把参数的实际值复制给函数的形式参数,在这种情况下,修改函数内的形式参数不会影响实际参数。

2.引用调用

#include
void swap(int *num1,int *num2);
int main(){
    int a,b;
    printf("请输入a,b的值:");
    scanf("%d%d",&a,&b);
    printf("交换前,a的值为: %d\n",a);
    printf("交换前,b的值为: %d\n",b);
    swap(&a,&b);
    printf("交换后,a的值为: %d\n",a);
    printf("交换后,b的值为: %d\n",b);
    return 0;
}

void swap(int *num1,int *num2){
    int temp;
    temp=*num2;
    *num2=*num1;
    *num1=temp;
}

【运行结果】

请输入a,b的值:1 2
交换前,a的值为: 1
交换前,b的值为: 2
交换后,a的值为: 2
交换后,b的值为: 1

【注】通过引用传递方式,形参为指向实参地址的指针,当对形参的指向操作时,就相当于对实参本身进行的操作。传递指针可以让多个函数访问指针所引用的对象,而不用把对象声明为全局可访问。
指针我们之后会讲到,这里特别注意*的作用:

  • 用来指定一个变量是指针
  • 一元运算符*来返回位于操作数所指定地址的变量的值

实例32:【字符串中删除指定字母】❤️

题目:删除一个字符串中的指定字母,如:字符串 "aca",删除其中的 a 字母。

01程序分析:
第1次:
02Bad Solution:

#include
#include
int main()
{
    char ch[100],a;
    int i,j;
    printf("请输入字符串:");
    gets(ch);
    printf("请输入要删除的字符:");
    scanf("%c",&a);
    for(i=0;i

【运行结果】

abax
请输入要删除的字符:a
删除 a 的字符串为:bx

03Correct Solution:

#include
#include
char * deleteCharacters(char *str,char charSet);
int main()
{
    char a;//要删除的字母
    char ch[100];//要删除的字符串
    printf("请输入字符串:");
    gets(ch);
    printf("请输入要删除的字符:");
    scanf("%c",&a);
    printf("%s\n",deleteCharacters(ch,a));
    return 0;
}

char  *deleteCharacters(char *str,char charSet) {
    if (charSet==NULL)
        return str;
    else if(charSet!=NULL) {
        for (int i = 0; i < strlen(str); i++) {
            if (str[i] == charSet) {
                for (int j = i; j < strlen(str); j++)//将后面元素往前移
                    str[j] = str[j + 1];
            }
        }
        return str;
    }
}

【运行结果】

请输入字符串:warning: this program uses gets(), which is unsafe.
abc
请输入要删除的字符:a
bc

04题目总结:
1.在 C 语言中,字符串实际上是使用 null 字符 '\0' 终止的一维字符数组。C 编译器会在初始化数组时,自动把 '\0' 放在字符串的末尾。
2.size_t strlen(const char *str)计算字符串 str 的长度,直到空结束字符,但不包括空结束字符。
3.char *gets(char *str)从标准输入 stdin 读取一行,并把它存储在 str 所指向的字符串中。当读取到换行符时,或者到达文件末尾时,它会停止,具体视情况而定。

实例33:【质数】

题目:判断一个数字是否为质数。

01程序分析:
第1次:写一个判断是否是质数的函数。
02Bad Solution:
暂无
03Correct Solution:

#include
int isPrime(int n)
{
    if(n<=1) return 0;
    for(int i=2;i

【运行结果】

1 是否质数 0
10 是否质数 0
19 是否质数 1

04题目总结:
1.一个函数可返回一个值,所以当调用函数时,执行到某一返回语句后,即返回主函数。

实例35:【字符串反转】❤️

题目:字符串反转,如将字符串 "www.runoob.com" 反转为 "moc.boonur.www"。

01程序分析:
第一次:编写一个反转函数。
02Bad Solution:
暂无
03Correct Solution:

【运行结果】

04题目总结:
1.将这道题与实例32放在一起对比。

====考点1:函数====【止】====

实例36:【素数】

题目:求100之内的素数。

01程序分析:
02Bad Solution:

#include
int main()
{
    int tag,n=0;
    for(int i=2;i<=100;i++) {
        for (int j = 2; j <= i; j++) {
            tag = j;
            if (i % j == 0) break;
        }
        if (tag == i)
        {
            printf(" %-3d ",i);
            n++;
            if(n%5==0) printf("\n");
        }
    }
    return 0;

【运行结果】

结果02

【缺陷】循环的次数多了,运行慢,可利用数学基础数论知识缩减循环次数。
03Correct Solution:

#include
#include
int main() {
    int i,j,k,n = 0;
    for (i = 2; i <= 100; i++) {
        k=(int)sqrt(i);
        for (j = 2; j <= k; j++)
            if (i % j == 0) break;
        if (j>k) {
            printf(" %-3d ", i);
            n++;
            if (n % 5 == 0) printf("\n");
        }
    }
    return 0;
}

【运行结果】
同“结果02”

04题目总结:

  1. 质数(prime number)又称素数,有无限个。一个大于1的自然数,除了1和它本身外,不能被其他自然数整除。

2.在for()里定义的是局部变量,只在for循环里面起作用。


——2.25更新——

实例37:【排序算法】❤️

题目:对10个数进行排序。

01知识储备:
第一次:
02Bad Solution:

#include
#define N 10
int main() {
    int i, j, a[N], min, temp;
    printf("请输入10位数:");
    for (i = 0; i < N; i++)
        scanf("%d", &a[i]);
    for (i = 0; i < N - 1; i++) {
        min = i;
        for (j = i + 1; j < N; j++) {
            if (a[min] > a[j]) {
                temp = a[min];
                a[min] = a[j];
                a[j] = temp;
            }
        }
    }
    printf("交换后的10位数为:");
    for (i = 0; i < N; i++)
        printf("%d ", a[i]);
    return 0;
}

【运行结果】

请输入10位数:11 22 9 8 7 6 5 4 3 2
交换后的10位数为:2 3 4 5 6 7 8 9 11 22

【缺陷】
这种方法运行起来更复杂,交换次数更多。会频繁交换元素,只是看起来代码少一点。
03Correct Solution:

#include
#define N 10
int main()
{
    int i,j,a[N],temp;
    printf("请输入10个数字:\n");
    for(i=0;ia[j]) min=j;
        if(min!=i)
        {
            temp=a[min];
            a[min]=a[i];
            a[i]=temp;
        }
    }
    printf("排序结果是:\n");
    for(i=0;i

【结果对比】
虽然02、03得到的结果是一样的,但是03在排序中,交换的次数更少。比如将4、3、2、1按从小打大的顺序排列。03是先找到最小那个,把它排到最前面,而02是找到比当前位置小的就交换,当然交换的次数多得多得多啦,这对电脑伤害很大。
04题目总结:
暂无

实例38:【二维数组】

题目:求一个3*3矩阵对角线元素之和

01知识储备:
二维数组
02Bad Solution:

#include
int main() {
    int a[3][3]={1,2,3,4,5,6,7,8,9},i,j,sum=0;
    for(i=0;i<3;i++)
        for(j=0;j<3;j++)
            if(i==j) sum=sum+a[i][j];
    printf("对角元素的和是: %d",sum);
    return 0;
}

【运行结果】

对角元素的和是: 15

【缺陷】
1.算对角线元素之和可以不用嵌套循环。
03Correct Solution:

#include
int main() {
    int a[3][3],i,j,sum=0;
    printf("请输入3*3矩阵:\n");
    for(i=0;i<3;i++)
        for(j=0;j<3;j++)
            scanf("%d",&a[i][j]);
    for(i=0;i<3;i++)
            sum+=a[i][i];
    printf("对角元素的和是: %d",sum);
    return 0;
}

【运行结果】

请输入3*3矩阵:
1 2 3 4 5 6 7 8 9

对角元素的和是: 15

04题目总结:
暂无

实例39:【插入】❤️

题目:有一个已经排好序的数组。现输入一个数,要求按原来的规律将它插入数组中。

01程序分析:
第一次:如果比最后一个数字大,则放在最后。否则,看比哪个数字小,将大的那部分全体向后移动。
02Bad Solution:

#include
int main() {
    int a[6]={1,3,4,7,8},num,i,j,temp1,temp2;
    printf("输入要插入的数字:");
    scanf("%d",&num);
    if(num>a[4]) {
        a[5] = num;
    }
    else{
        for(i=0;i<5;i++)
        {
            if(num

【运行结果】

输入要插入的数字:5
插入后的数组为:1 3 4 7 8 5

【错因】
没有起到插入作用。Why?
仔细分析下这个程序,发现有以下错误:
for(j=5;j中的循环条件错了,应该为for(j=5;j>i;j--)
a[i]=num;这个应该在if条件句里面,将后面的数移动了后立即插入。
03Correct Solution:
①从后往前移

#include
int main() {
    int a[6]={1,3,4,7,8},num,i,j,temp1,temp2;
    printf("输入要插入的数字:");
    scanf("%d",&num);
    if(num>a[4]) {
        a[5] = num;
    }
    else{
        for(i=0;i<5;i++)
        {
            if(numi;j--)
                    a[j]=a[j-1];
                a[i]=num;
                break;
            }
        }
    }
    printf("插入后的数组为:");
    for(i=0;i<6;i++)
        printf("%d ",a[i]);
}

【运行结果】

输入要插入的数字:2
插入后的数组为:1 2 3 4 7 8

②从前往后移

#include
int main()
{
    int a[11]={1,4,6,9,13,16,19,28,40,100};
    int temp1,temp2,number,end,i,j;
    printf("原始数组是:\n");
    for(i=0;i<10;i++)
        printf("%4d",a[i]);
    printf("\n插入一个新的数字: ");
    scanf("%d",&number);
    end=a[9];
    if(number>end)
        a[10]=number;
    else
    {
        for(i=0;i<10;i++)
        {
            if(a[i]>number)
            {
                temp1=a[i];
                a[i]=number;
                for(j=i+1;j<11;j++)
                {
                    temp2=a[j];
                    a[j]=temp1;
                    temp1=temp2;
                }
                break;
            }
        }
    }
    for(i=0;i<11;i++)
        printf("%4d",a[i]);
    printf("\n");
    return 0;
}

04题目总结:
1.数组的长度是在定义的时候确定的,不同编译器对于数组越界的处理也是不一样的,一般而言数组的长度是不可以改变的。

  • 有一些编译器对于数组越界,是不处理,继续放后面。但是会对后面的数据进行改变。
  • 还有一些编译器就是程序遇到未知错误而终止。但不管是哪一种,都需要注意数组的下标是不是越界。

实例40:【数组逆序】

题目:将一个数组逆序输出(要求逆序,不能直接从后往前输出)。

01程序分析:
第一次:将数组的第一个数与最后一个数交换,偶数个数的数列和奇数个数的数列处理方法相同。
02Bad Solution:
暂无
03Correct Solution:

#include
#define N 10
int main()
{
    int a[N]={1,2,3,4,5,6,7,8,9,10},i,temp;
    for(i=0;i

【运行结果】

逆转后的序列为:10 9 8 7 6 5 4 3 2 1

04题目总结:
暂无

你可能感兴趣的:(学习日记 | C语言经典例题②(实例21-40))