# NENU 程序设计基础C语言 PTA(6~9章)

这里是大一入学的一门必修,会实现最最基础的C语言内容。

# 前言

作为初学者,可以去各种平台上学习C语言的语法。

善用搜索引擎、代码论坛(CSDN、稀土掘金、stack overflow)、AI(国内的kimi、文心一言、通义千问)进行学习。

可以学习笔者的代码风格,也可以不学习,但是推荐代码里的符号,例如+-*/=的前后都加上空格。

代码换行要注意,以及括号换行也要注意,请尽量保持一种代码风格,不要出现如下这种代码:

#include<stdio.h>
int main()
{
 int n;
 scanf("%d",&n);
 for(int i = 1;i <= n;i++){ printf("%d",i);}
 for(int i = n;i >= 1;i--){
     for(int j = 1;j <= i;j++)
     {if(j < i) printf("%d ",j);
     }
     printf("\n");
 }
}

注意学会看报错信息,error开头的那种,不了解就先去搜索,明白”提问的艺术“。

有些字符数组的处理需要用到头文件<string.h>,有些用到根号sqrt计算的需要用到头文件<math.h>。

fgets与gets的区别和作用需要读者自行查询学习!!

# 6-1 求最大值及其下标

# 思路:

这是一个思维方式:用一个变量专门存储要求的结果。

这里我们可以用两个变量,分别存储最大值和其对应的下标,判断方式就是判断大小。

# 代码C语言:

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

# 6-2 将数组中的数逆序存放

# 思路:

这道题我们就按照题目的意思来实现就行~

注意格式问题!!

# 代码C语言:

#include<stdio.h>
int main(){
    int n;
    scanf("%d",&n);
    int a[12];
    for(int i = 0;i < n;i++){
        scanf("%d",&a[i]);
    }
    printf("%d",a[n - 1]);
    for(int i = n - 2;i >= 0;i--){
        printf(" %d",a[i]);
    }
}

# 6-3 选择法排序

# 思路:

这道题是选择法排序。

我们的思路是每次遍历整个数组,然后把最大的元素放在前面。

这样我们每次都能找到最大的,对于全局来说,就是第一次找到最大的,第二次又找到最大的,所以我们应该是每次在剩余没有确定的部分里寻找,我们每次都找最大的放前面,然后就有了第一个最大、第二个最大、第三个最大……这样我们就能从大到小排序了,每一次我们可以找到一个最大的数字,也就是说,我们每次可以完全确认一个数字的位置是正确的。

具体看代码~

# 代码C语言:

#include<stdio.h>
int main(){
    int n,a[11];
    scanf("%d",&n);
    for(int i = 0;i < n;i++){
        scanf("%d",&a[i]); // 将n个整数存入数组
    }
    int temp = 0;
    int maxx = 0;
    // 接下来要比较大小,由大到小依次输出
    // 选择法排序:从头到尾扫描序列,找出最大的元素与第一个元素交换
    // 然后从剩下的部分中重复此行为
    for(int i = 0;i < n;i++){
        maxx = i;
        for(int j = i + 1;j < n;j++){
            if(a[maxx] < a[j]) {
                maxx = j; 
            }
        }
        temp = a[maxx];
        a[maxx] = a[i];
        a[i] = temp;
    }
    printf("%d",a[0]);
    for(int i = 1;i <= n - 1;i++){
        printf(" %d",a[i]);
    }
    return 0;
}

# 6-4 冒泡法排序

# 思路:

冒泡排序一定要搞明白,很多面试题喜欢问~

题目已经讲得很明白了:从头到尾比较相邻两个元素,如果前面的元素大于其紧随的后面元素,则交换它们。通过一遍扫描,则最后一个元素必定是最大的元素。然后用同样的方法对前N−1个元素进行第二遍扫描。依此类推,最后只需处理两个元素,就完成了对N个数的排序。

所以我们这样去实现即可~

# 代码C语言:

#include<stdio.h>
int main(){
    int n,a[1001];
    int k;
    scanf("%d %d",&n,&k);
    for(int i = 0;i < n;i++){
        scanf("%d",&a[i]);
    }
    // 这里进行n - 1次排序即可,因为每次确认一个数字,n - 1次已经确定了n - 1个数字的位置,最后一个数字的位置一定也是确定的
    for(int i = 0;i < n - 1;i++){
        // 已经排序k次了
        if(i == k) break;
        // 这里就是冒泡的核心,每次把最大的往后面放,然后因为我们只遍历未确定位置的部分,所以内循环是n - 1次再减去i次(因为进行i次说明放在最后i个位置的数字已经被确定了
        for(int j = 0;j < n - 1 - i;j++){
            if(a[j] > a[j + 1]){
                int temp = a[j + 1];
                a[j + 1] = a[j];
                a[j] = temp;
            }
        }
    }
    // 注意行尾空格的格式问题
    printf("%d",a[0]);
    for(int i = 1;i <= n - 1;i++){
        printf(" %d",a[i]);
    }
}

# 6-5 fibonacci数列

# 思路:

这道题就是按题目意思处理即可,记得先赋初值,然后就可以进行递推了,然后把前12个数字都计算出来之后,注意按格式输出~

# 代码C语言:

#include<stdio.h>
int main(){
    int a[100];
    a[0] = 1,a[1] = 1;
    for(int i = 2;i < 12;i++){
        a[i] = a[i - 1] + a[i - 2];
    }
        
    for(int i = 0,j = 1;i < 12;i++,j++){
        printf("%6d",a[i]);
        if(j % 3 == 0){
            printf("\n");
        }
    }
}

# 6-6 数组中插入一个数

# 思路:

这道题笔者发现自己大一的时候居然真的去模拟了一遍插入的过程hhh

其实这道题我们就判断,输入的这个数字,假设插入第 i 个位置,那么是否第 i - 1个数字是小于等于它的,第 i + 1个数字是大于等于它的,如果满足,就输出出来即可。【注意只能输出一次,而且最前面和最后面的情况需要特殊考虑】

此外,读者也可以复习刚刚的排序,即把数字放进去,然后再全部排序一次,这个写法也是可以的~

由此便可以有三种写法啦~读者自行思考!

下面给出的就是直接输出的做法!

# 代码C语言:

#include<stdio.h>
int main(){
    int a[12] = {1, 2, 4, 6, 8, 9, 12, 15, 149, 156};
    int n;
    scanf("%d",&n);
    // 最前面的情况
    if(n < a[0]){
        printf("%5d",n);
        for(int i = 0;i < 10;i++){
            printf("%5d",a[i]);
        }
    }else if(n > a[9]){ // 最后面的情况
        for(int i = 0;i < 10;i++){
            printf("%5d",a[i]);
        }
        printf("%5d",n);
    }else{
        // 用flag表示是否已经输出
        int flag = 1;
        printf("%5d",a[0]);
        for(int i = 1;i < 10;i++){
            if(flag == 1 && n >= a[i - 1] && n <= a[i]){
                printf("%5d",n);
                flag = 0;
            }
            printf("%5d",a[i]);
        }
    }
    
}

# 6-7 数组元素的删除

# 思路:

这道题我们就按照题目的要求来进行删除,也就是每次把要删除的数字的后面所有数字都向前平移。

具体看代码实现~

# 代码C语言:

#include<stdio.h>
int main(){
    int n;
    int a[102];
    int cnt = 0; // 记录数组长度
    scanf("%d",&n);
    cnt = n;
    for(int i = 1;i <= n;i++){
        scanf("%d",&a[i]);
    }
    int k;
    int index; // 记录要被删除的数字的下标
    scanf("%d",&k);
    while(k--){
        cnt--;
        scanf("%d",&index);
        for(int i = index;i <= n - 1;i++){
            a[i] = a[i + 1];
        }
    }
    printf("%d",a[1]);
    for(int i = 2;i <= cnt;i++){
        printf(" %d",a[i]);
    }
}

# 6-8 查找满足条件的所有整数

# 思路:

这道题我们的思路就是,把数组a中的每个数字都与要找的数字x进行比较,然后如果是,我们就输出。

我们可以用一个变量记录我们是否已经输出过数字【即是否已经找到了】,如果最后没找到,就输出“Not Found”。

# 代码C语言:

#include<stdio.h>
int main(){
    int n,x;
    scanf("%d %d",&n,&x);
    int a[12];
    int flag = 1;
    for(int i = 1;i <= n;i++){
        scanf("%d",&a[i]);
        if(a[i] == x){
            printf("%d\n",i - 1);
            flag = 0;
        }
    }
    if(flag){
        printf("Not Found\n");
    }
}

# 6-9 求矩阵各行元素之和

# 思路:

这道题我们就模拟题目的意思,即有m行,每行有n个读入,我们用一个变量记录每一行的和即可。

如果读者想要用二维数组去存储,然后再遍历也是可以的,但是笔者这里为了减少代码量,就采取了下面的写法~

# 代码C语言:

#include<stdio.h>
int main(){
    int m,n;
    scanf("%d %d",&m,&n);
    for(int i = 1;i <= m;i++){
        int sum = 0; // 记录每一行的和
        for(int i = 1;i <= n;i++){
            int temp = 0; // 临时存储每一个变量
            scanf("%d",&temp);
            sum += temp;
        }
        printf("%d\n",sum);
    }
}

# 6-10 找鞍点

# 思路:

这道题可能对于初学者来说有点难度,要怎么判断这个数字是行最大且列最小的呢?

这里推荐采取笔者的写法,比较易于理解【相较于其他题解】

笔者的思路就是,我们先假设每个点都是鞍点,所以我们就把每个数字都遍历一遍,每个数字被遍历到的时候,就去这一行判断是不是最大的,再去这一列判断是不是最小的,如果两个条件都满足,我们就输出结果。

如果我们全程没发现鞍点,就输出NONE。

# 代码C语言:

#include<stdio.h>
int main(){
    int a[8][8];
    int n;
    scanf("%d",&n);
    for(int i = 1;i <= n;i++){
        for(int j = 1;j <= n;j++){
            scanf("%d",&a[i][j]);
        }
    }
    int flag = 1; // 记录是否存在鞍点,默认不存在鞍点为1
    for(int i = 1;i <= n;i++){
        for(int j = 1;j <= n;j++){
            // 这个时候判断这个数字是不是这一行最大的
            int flag1 = 1; // 如果是最大的就是1,如果不是就是0,我们假设这个数字就是行最大的
            int maxx = a[i][j]; // 假设是最大的
            for(int k = 1;k <= n;k++){
                if(a[i][k] > maxx){
                    flag1 = 0;
                }
            }
            // 这个时候判断这个数字是不是这一列最小的
            int flag2 = 1; // 如果是最大的就是1,如果不是就是0,我们假设这个数字就是列最小的
            int minn = a[i][j]; // 假设是最小的
            for(int k = 1;k <= n;k++){
                if(a[k][j] < minn){
                    flag2 = 0;
                }
            }

            if(flag1 && flag2){
                flag = 0;
                printf("%d %d",i - 1,j - 1);
            }
        }
    }
    if(flag){
        printf("NONE");
    }
}

# 6-11 统计一行文本的单词个数

# 思路:

这道题我们就判断单词+空格情况即可,对于最后一个单词,我们就特别判断一下~

这道题的实现方式应该有其他的,笔者直接放自己大一时候的代码了~

# 代码C语言:

#include<stdio.h>
int main(){
    char a[1000]; // 用来放字符串 
    fgets(a,sizeof(a),stdin); // 读入一整行
    int len,i;
	int num = 0;
    for(i = 0;a[i] != '\0';i++); // 循环到输入终止 

    len = i; // 记录输入的总长度 
    	
    for (i = 0;i < len;i++){
        if (a[i] != ' ' && a[i+1] == ' ') num++; // 防止连续空格 
        if (a[i] != ' ' && a[i+1] == '\0') num++; // 防止结尾空格 
    }
    printf("%d",num);
}

# 7-1 近似求PI

# 思路:

这道题就是按照题目给的公式来,我们很容易发现,第 i 项式子的结果就是第 i - 1项的结果乘以 i /(2 * i + 1),所以我们便可以推出这道题的写法~

# 代码C语言:

#include<stdio.h>
double cal(double eps){
    double pi = 1,n = 1;
    for(int i = 1;eps <= n;i++){
        n = n * i / (2 * i + 1);
        pi = pi + n;
    }
    return 2 * pi;
}

int main(){
    double eps;
    scanf("%le",&eps);
    printf("PI = %.5lf",cal(eps));
}

# 7-2 判断素数

# 思路:

判断素数的题目很经典~

代码一定要会写哦!我们就对于每个数字,都从2~sqrt(n)中判断是否可以整除,如果可以说明不是素数,否则就是素数。

# 代码C语言:

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

int main(){
    int n;
    scanf("%d",&n);
    while(n--){
        int a;
        scanf("%d",&a);
        if(judge(a)){
            printf("Yes\n");
        }else{
            printf("No\n");
        }
    }
}

# 7-3 判断数字字符

# 思路:

这道题太简单,不讲解~

# 代码C语言:

#include<stdio.h>
#include<math.h>
int judge(char ch){
    if(ch >= '0' && ch <= '9'){
        return 1;
    }
    return 0;
}

int main(){
    char c;
    scanf("%c",&c);
    if(judge(c)){
        printf("yes");
    }else{
        printf("no");
    }
}

# 7-4 汉诺塔问题

# 思路:

汉诺塔问题也非常的经典~

笔者这里也没办法讲清楚这个策略,所以推荐读者自行查询学习!

# 代码C语言:

#include<stdio.h>
void han(int n,char a[],char b[],char c[]){
	if (n == 1){
		printf("%s->%s\n",a,c);
	}
	else{
		han(n - 1,a,c,b); // 将A座上的n-1个盘子借助C座移向B座
		printf("%s->%s\n",a,c); // 将A座上最后一个盘子移向C座
		han(n - 1,b,a,c); // 将B座上的n-1个盘子借助A座移向C座
	} 
}

int main(){
    int n;
    char a[100],b[100],c[100];
    scanf("%d %s %s %s",&n,a,b,c);
    han(n,a,b,c);
}

# 7-5 计算函数P(n,x)

# 思路:

这道题就是注意精度与格式~

# 代码C语言:

#include<stdio.h>
double p(int n,double x){
    if(n == 0)
        return 1;
    if(n == 1)
        return x;
    if(n > 1)
        return ((2 * n - 1) * p(n - 1,x) - (n - 1) * p(n - 2,x)) / n;
}

int main(){
    int n;
    double x;
    int rep;
    scanf("%d",&rep);
    for(int i = 0;i < rep;i++){
        scanf("%d %lf",&n,&x);
        printf("p(%d,%.2lf)=%.2lf\n",n,x,p(n,x));
    }
}

# 8-1 输出学生成绩

# 思路:

这道题也是按照题意即可,把成绩存储起来,计算平均值、记录最大、最小值即可!

# 代码C语言:

#include<stdio.h>
#include<cstdlib>
int main(){
    int n;
    double *s;
    double sum = 0,ave = 0,maxx = 0,minn = 0;
    scanf("%d",&n);
    s = (double*)malloc(sizeof(double)*n);
    for(int i = 0;i < n;i++){
        scanf("%lf",s + i);
    }
    maxx = minn = s[0];
    for(int i = 0;i < n;i++){
        sum = sum + s[i]; 
        if (s[i] < minn)
            minn = s[i];
        if (s[i] > maxx)
            maxx = s[i];
    }
    ave = sum / n;
    printf("average  =  %.2lf\nmaxx  =  %.2lf\nminn  =  %.2lf",ave,maxx,minn);
}

# 8-2 组织星期信息

# 思路:

按题意模拟~

# 代码C语言:

#include<stdio.h>
#include<string.h>
int main(){
    int repeat;
    char *a[7] = {"Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"};
    char str[100][100];
    scanf("%d",&repeat);
    for(int i = 1;i <= repeat;i++){
        scanf("%s",str[i]);
    }
    for(int i = 1;i <= repeat;i++){
        int index = 0;
        for(int j = 0;j < 7;j++){    
            if(strcmp(str[i],a[j]) == 0){
                index = j + 1; // c存储遍历的下标
            }
        }
        if(index != 0){
            printf("%d\n",index);
        }else{
            printf("-1\n");
        }
    }
}

# 9-1 查找书籍

# 思路:

结构体的运用~

这道题就是用结构体处理,然后找最大最小的即可。

注意数组大小,可以适当开大一点~

# 代码C语言:

#include<stdio.h>
#include<string.h>
struct book{
    char name[32];
    double price;
}book[100];

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

    int index = 1;
    double price;

    for(int i = 1;i <= n;i++){
        fgets(book[i].name,sizeof(book[i].name),stdin);
        scanf("%lf",&book[i].price);
        getchar();
    }

    price = book[1].price;

    for(int i = 1;i <= n;i++){
        if(book[i].price > price){
            index = i;
        }
    }
    printf("%.2lf, %s",book[index].price,book[index].name);

    price = book[1].price;

    for(int i = 1;i <= n;i++){
        if(book[i].price < price){
            index = i;
        }
    }
    printf("%.2lf, %s",book[index].price,book[index].name);
}