# 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);
}