实验三

实验三

任务1

task1.c源代码

点击查看代码
#include <stdio.h>

char score_to_grade(int score);  // 函数声明

int main() {
    int score;
    char grade;

    while(scanf("%d", &score) != EOF) {
        grade = score_to_grade(score);  // 函数调用
        printf("分数: %d, 等级: %c\n\n", score, grade);
    }

    return 0;
}

// 函数定义
char score_to_grade(int score) {
    char ans;

    switch(score/10) {
    case 10:
    case 9:   ans = 'A'; break;
    case 8:   ans = 'B'; break;
    case 7:   ans = 'C'; break;
    case 6:   ans = 'D'; break;
    default:  ans = 'E';
    }

    return ans;
}

运行测试截图

task1

回答问题

问题1:函数score_to_grade的功能是什么?形参类型、返回值类型是什么?

函数score_to_grade的功能是判断分数对应的等级并返回该等级对应的值。形参类型为int型(整型),返回值类型为char型(字符型)。

问题2:如果line21-28以下形式,代码存在哪些问题,请逐一指出。

点击查看代码
switch(score/10) {
	case 10:
	case 9: ans = "A";
	case 8: ans = "B";
	case 7: ans = "C";
	case 6: ans = "D";
	default: ans = "E";
}

源代码line23-26的语句后都跟随一个break语句,在执行完ans的赋值语句之后,跳出整个switch结构。而修改后的代码缺少break语句,会导致执行完ans的赋值语句后,会无视后续case的条件,直接继续执行下一个case中的语句,直到switch结束。在task1.c中,修改后会导致ans被多次赋值,最终赋值为字符E,运行测试如下图。

task1_2

任务2

task2.c源代码

点击查看代码
#include <stdio.h>

int sum_digits(int n);  // 函数声明

int main() {
    int n;
    int ans;

    while(printf("Enter n: "), scanf("%d", &n) != EOF) {
        ans = sum_digits(n);    // 函数调用
        printf("n = %d, ans = %d\n\n", n, ans);
    }

    return 0;
}

// 函数定义
int sum_digits(int n) {
    int ans = 0;

    while(n != 0) {
        ans += n % 10;
        n /= 10;
    }

    return ans;
}

运行测试截图

回答问题

问题1:函数sum_digits的功能是什么?

计算各位数字之和。

问题2:如果把函数sum_digits定义成如下实现方式,能实现同样的输出吗?如果能,说明两种实现方式的算法思维区别;如果不能,分析原因。

点击查看代码
int sum_digits(int n) {
	if(n < 10)
		return n;

	return sum_digits(n/10) + n%10;
}

两段代码能实现相同的输出,但实现方式不同。
原代码(迭代法):采用循环结构以 n自身作为边界判定条件,循环累加。
新代码(递归法):采用函数自身调用,将整数通过n/10逐位拆解,并以if(n<10)作为递归边界条件,最终将各数位(n%10)累加返回。”

任务3

task3.c源代码

点击查看代码
#include <stdio.h>

int power(int x, int n);    // 函数声明

int main() {
    int x, n;
    int ans;

    while(printf("Enter x and n: "), scanf("%d%d", &x, &n) != EOF) {
        ans = power(x, n);  // 函数调用
        printf("n = %d, ans = %d\n\n", n, ans);
    }

    return 0;
}

// 函数定义
int power(int x, int n) {
    int t;

    if(n == 0)
        return 1;
    else if(n % 2)
        return x * power(x, n-1);
    else {
        t = power(x, n/2);
        return t*t;
    }
}

运行测试截图

回答问题

问题1:函数power的功能是什么?

计算x的n次幂。

问题2:函数power是递归函数吗?如果是,找出递归模式,写出其对应的数学公式模型。(公式中须体现递归出口)

函数power是递归函数。对应的数学公式如下:

\[power(x, n) = \begin{cases} 1, & n = 0 \\ x \cdot power(x, n-1), & n \bmod 2 \equiv 1 \\ \big(power(x, n/2)\big)^2, & n \bmod 2 \equiv 0 \text{ and } n > 0 \end{cases} \]

问题3:如果对line26-27做改动:t=power(x,n/2);return t*t;,改成:return power(x,n/2) * power(x,n/2);

① 算法逻辑上是否仍然正确?为什么?

仍然正确。原函数将power(x,n/2)的值赋给变量t,返回值返回t*t,与新函数中power(x,n/2) * power(x,n/2)是等价的,虽然求值过程不同,但返回值完全相同。

② 与原写法相比,从性能角度分析有何不同?请从"子问题是否重复计算"的角度分析,并估算两种写法的递归调用次数(以计算 power(3,16) 为例)。

原写法比新写法更加高效。原算法power(x,n/2)的结果被保存到变量t中,后续直接复用t*t,每个子问题仅计算一次,递归调用形成一条单链;新写法每次偶数分支都会独立地触发两次power(x,n/2)调用,产生大量冗余计算。递归调用展开为满二叉树结构。原算法的时间复杂度更低。
以power(3,16)为例估算两种写法发递归调用次数:
原写法:power(3,16)→power(3,8)→power(3,4)→power(3,2)→power(3,1)
共调用5次。
新写法:共调用31次,调用方式如下图:

task3power

任务4

task4.c源代码

点击查看代码
#include<stdio.h>
int a,b,c;
int classify_triangle(int a,int b,int c)
{
	if(a+b>c&&a+c>b&&b+c>a)	//判断是不是三角形
	{
		if(a==b&&b==c)	//判断是不是等边三角形
		return 2;
		else
		{
			if(a==b||b==c||a==c)	//判断是不是等腰三角形
			return 3;
			else
			{
				if(a*a+b*b==c*c||a*a+c*c==b*b||a*a==b*b+c*c)	//判断是不是直角三角形
				return 4;
				else
				return 1;	//上面的情况都不满足,一般三角形
			}
		}
	}
	else
	return 0;
}
int main()
{
	while( scanf("%d%d%d",&a,&b,&c)!=EOF)
	{
		switch(classify_triangle(a,b,c))
		{
			case 0:printf("不能构成三角形\n");break;
			case 1:printf("普通三角形\n");break;
			case 2:printf("等边三角形\n");break;
			case 3:printf("等腰三角形\n");break;
			case 4:printf("直角三角形\n");
		}
	}
	return 0;
}

运行测试截图

task4

任务5

task5_1.c源代码(迭代方式)

点击查看代码
#include <stdio.h>
int func(int n, int m);   // 函数声明

int main() {
    int n, m;
    int ans;

    while(scanf("%d%d", &n, &m) != EOF) {
        ans = func(n, m);   // 函数调用
        printf("n = %d, m = %d, ans = %d\n\n", n, m, ans);
    }

    return 0;
}

int func(int n,int m)	// 函数定义
{
	int ans,s1=1,s2=1,i;
	for(i=n;i>=n-m+1;i--)
	s1*=i;
	for(i=2;i<=m;i++)
	s2*=i;
	ans=s1/s2;
	return ans;
}

运行测试截图

task5_1

task5_2.c源代码(递归函数)

点击查看代码
#include <stdio.h>
int func(int n, int m);   // 函数声明

int main() {
    int n, m;
    int ans;

    while(scanf("%d%d", &n, &m) != EOF) {
        ans = func(n, m);   // 函数调用
        printf("n = %d, m = %d, ans = %d\n\n", n, m, ans);
    }

    return 0;
}

int func(int n, int m)	// 函数定义
{
	if(m>n)
	return 0;
	else if(m==n||m==0)
	return 1;
	else
	return func(n-1,m)+ func(n-1,m-1);
}

运行测试截图

task5_2

任务6

task6.c源代码

点击查看代码
#include <stdio.h>

int gcd(int a,int b,int c);// 函数声明


int main() {
    int a, b, c;
    int ans;

    while(scanf("%d%d%d", &a, &b, &c) != EOF) {
        ans = gcd(a, b, c);     // 函数调用
        printf("最大公约数: %d\n\n", ans);
    }

    return 0;
}


int gcd(int a,int b,int c)// 函数定义
{
	int m;
	if(a>=b&&c>=b)
	m=b;
	if(b>=a&&c>=a)
	m=a;
	if(a>=c&&b>=c)
	m=c;
	for(;m>=1;m--)
	{
		if(a%m==0&&b%m==0&&c%m==0)
		return m;
	}
}

运行测试截图

task6

任务7

task7.c源代码

点击查看代码
#include <stdio.h>
#include <stdlib.h>

void print_charman(int n);// 函数声明


int main() {
    int n;

    printf("Enter n: ");
    scanf("%d", &n);
    print_charman(n); // 函数调用

    return 0;
}

void print_charman(int n)// 函数print_charman定义
{
	int flag=1,i,j,k=n*2-1;
	for(i=1;i<=n;i++)
	{
		if(i==1)
		k--;
		if(i==1)
			printf(" O");
		for(j=2;j<=i-1;j++)
			printf("\t");
		for(j=1;j<=k;j++)
			printf("\t O");
		printf("\n");
		if(i==1)
			printf("<H>");
		for(j=2;j<=i-1;j++)
			printf("\t");
		if(i!=1)
			printf("   ");
		for(j=1;j<=k;j++)
			printf("     <H>");
		printf("\n");
		if(i==1)
			printf("I I");
		for(j=2;j<=i-1;j++)
			printf("\t");
		if(i!=1)
			printf("   ");
		for(j=1;j<=k;j++)
			printf("     I I");
		printf("\n");
		if(i!=1)
		k-=2;
		else
		k--;
	}
	return ;
}

运行测试截图

task7_1

task7_2

task7_3

task7_4

实验总结

关于递归算法和迭代算法

对比维度 迭代 递归
定义 通过循环结构(for / while)反复执行代码块,直到条件不成立 函数直接或间接调用自身,把问题分解为结构相同的子问题
核心思想 逐步逼近,由初始状态反复更新到目标状态 分而治之,自顶向下分解+回溯合并结果
控制结构 循环语句(for/while/do-while) 选择语句(if-else)+函数自身调用
终止条件 循环条件为false时退出 递归出口被触发时返回
状态传递 通过局部变量在循环中就地更新 通过函数参数向下一层传递,返回值向上传递
时间复杂度 通常较低,无函数调用开销 一般略高;会存在重复子问题
空间复杂度 仅占用少量循环变量 与递归深度成正比
栈溢出风险 无 有
代码可读性 逻辑线性,但状态多时较冗长、易出错 简洁优雅,与数学定义/树形结构高度贴合
主要优点 效率高、空间省、无溢出风险、易被底层优化 代码简洁、逻辑清晰、天然匹配递归定义的问题
主要缺点 部分问题实现繁琐 调用开销大、受栈深度限制、重复子问题性能差

两种算法在某些情况下可以相互转换(例如任务5)。

关于转义字符

任务7中使用到转义字符\t水平制表符。常用转义字符如下表:

转义字符 名称 十进制值 作用说明
\n 换行(LF) 10 光标移到下一行行首,最常用的换行符
\t 水平制表符(HT) 9 跳到下一个制表位(默认间隔 8 列),用于对齐
\r 回车(CR) 13 光标回到当前行行首,不换行
\b 退格(BS) 8 光标左移一格,不删除字符
\f 换页(FF) 12 换页,终端中常表现为清屏或翻页
\v 垂直制表(VT) 11 垂直制表,多数终端无可见效果
\a 响铃(BEL) 7 触发蜂鸣或闪屏,不移动光标
\\ 反斜杠本身 92 输出一个字面的 \
\' 单引号 39 在字符常量中表示单引号本身
\" 双引号 34 在字符串中表示双引号本身
\0 空字符(NUL) 0 字符串结束标志,不显示任何内容
posted @ 2026-11-13 17:13  Balena  阅读(7)  评论(0)    收藏  举报