C语言笔记第02章:三大基本结构(三)

简介: C语言笔记第02章:三大基本结构

3.6 总结

3.6.1 for循环与while循环对比

可以发现在while循环中依然存在循环的三个必须条件,但是由于风格的问题,使得三个部分很可能偏离较远,这样查找修改不够集中、方便。所以,for循环的风格更胜一筹。for循环使用的频率也最高。

3.6.2 while 与 do…while的区别

  • while :先循环条件判断执行循环操作
  • do…while :先执行循环操作循环条件判断

3.6.3 拓展练习

❓ 问下列语句循环多少次

#include <stdio.h>
int main()
{
    int i = 0;
    int k = 0;
    for (i = 0, k = 0; k = 0; i++, k++)
    {
        k++;
    }
    return 0; 
}

✏️ 循环0次。 k = 0 是 赋值操作。而在C语言当中,0代表false,1代表true,因此 k = 0 是将 0 赋值给 k,这是k的值就为0了,因此不执行循环。

4.章节练习

拓展方案是算法优化,感兴趣的可以琢磨一下。如果现在看不懂也没关系,记得以后学到了某个地方突然想起了一定要回来看它,就像那个女孩依旧在原地等你,不要辜负她❤️,她其实愿意等你好久好久。

4.1 计算阶乘

🏠 原题链接: https://www.luogu.com.cn/problem/P5739

4.1.1 题目描述

求 n!,也就是 1×2×3⋯×n

4.1.2 代码

#include<stdio.h>
int main() {
  int n; //求 n 的阶乘
  int res = 1; //阶乘的结果存储在res("result"的缩写)中,一定要赋值为1
  scanf_s("%d", &n);
  for (int i = 1; i <= n; i++) {
    res = res * i;//将 res * i 的值赋给 res
  }
  printf("res = %d", res);
  return 0;
}

4.1.3 拓展方案:函数递归

#include<stdio.h>
/*
  fun()函数的大括号中(代码块)的内容也可以用三元运算符来简写:
  return n > 1 ? n * fun(n - 1) : 1;
*/ 
int fun(int n)
{
  if (n > 1)
    return n * fun(n - 1);
  else //n = 1时就结束递归,返回1即可
    return 1;
}
int main() {
  int n; //求 n 的阶乘
  scanf_s("%d", &n);
  int res = fun(n); //阶乘的结果存储在res("result"的缩写)中
  printf("res = %d", res);
  return 0;
}

❓ 留下一个问题,如果我们设置的 n 过大,比如为1000,那这个res显然用int是无法装下的,即使用 long long int 类型也是无法容纳下那么大一个数的,那应该怎么解决呢?

✏️ 这个问题,可以用数组来解决,因此我们放到数组章节再来探讨这道题目。

4.2 素数个数

🏠 原题链接:https://www.luogu.com.cn/problem/P3912

4.2.1 题目描述

求1到1000中素数的个数。

4.2.2 思路分析

判断一个数是不是素数,我们首先得知道素数的定义:除了1和它本身,不能被其它自然数整除的数是素数,因此遍历每一个数,再对每一个数进行除法运算,看它能否被除1和它自身的数整除。我们又知道素数肯定不是偶数,所以我们循环遍历的时候每次循环迭代+2

4.2.3 代码

4.2.3.1 简单方法

#include<stdio.h>
int main() {
  int i, j;
  int isPrimerNumber;
  // 2 一定是素数,提前打印输出
    printf("2\n");
  // i = i + 2 保证了 i 一定是奇数
  for (i = 3; i < 1000; i = i + 2)//也可以写 i += 2 [复合赋值运算符]
  {
    //默认1即true,表示为素数
    isPrimerNumber = 1;
    /*
      1.对每个 i 进行判断是否为素数
      2.循环变量初始化
        j = 3:从3开始取模,为什么不为1和2
        - j=1,则肯定能取模为0,且不管是否为素数,肯定能被1和自身整除
        - j=2,我们已经在外层循环筛选出 i 肯定不为偶数因此没必要。
    */
    for (j = 3; j <= sqrt(i); j++)
    {
      if (i % j == 0)// 取模运算符:% 
      {
        //如果 i 能对 j 取模的结果为0,即说明j能被i整除,标记一下false,为非素数
        isPrimerNumber = 0;
        //退出内层循环,因为已经判断出了此时 i 不为素数,也没有继续循环下去的必要了
        break;
      }
    }
    if (isPrimerNumber)
    {
      //则说明 i 是素数
      printf("%d\n", i);
    }
  }
  return 0;
}

4.2.3.2 稍作优化:sqrt函数

判断 i 是否能被 2~i \sqrt{i}i 间的整数整除:

  • 输入的数n不能被 2~i \sqrt{i}i 间的整数整除,说明是素数
  • 输入的数n能被 2~i \sqrt{i}i 间的整数整除,说明不是素数

❓ 为什么可以进行这样的范围缩小呢?

对一个数n,如果他能分解成n=pq,那么pq里必然有一个大于等于根号n一个小于等于根号n,也就是说一个合数n必然有一个因子是小于等于根号n的. 所以对一个数n,只要检验他有没有小于等于根号n的因子就可以了(检验小于等于n的因子使循环次数变少,这也是简化的原因)

#include<stdio.h>
#include<math.h> //使用sqrt开平方根函数一定要记得引入math库
int main() {
  int i, j;
  int isPrimerNumber;
  // 2 一定是素数,提前打印输出
    printf("2\n");
  // i = i + 2 保证了 i 一定是奇数
  for (i = 3; i < 1000; i = i + 2)//也可以写 i += 2 [复合赋值运算符]
  {
    //默认1即true,表示为素数
    isPrimerNumber = 1;
    /*
      1.对每个 i 进行判断是否为素数
      2.循环变量初始化
        j = 3:从3开始取模,为什么不为1和2
        - j=1,则肯定能取模为0,且不管是否为素数,肯定能被1和自身整除
        - j=2,我们已经在外层循环筛选出 i 肯定不为偶数因此没必要
    */
    for (j = 3; j <= sqrt(i); j++) 
    {
      if (i % j == 0)// 取模运算符:% 
      {
        //如果 i 能对 j 取模的结果为0,即说明j能被i整除,标记一下false,为非素数
        isPrimerNumber = 0;
        //退出内层循环,因为已经判断出了此时 i 不为素数,也没有继续循环下去的必要了
        break;
      }
    }
    if (isPrimerNumber)
    {
      //则说明 i 是素数
      printf("%d\n", i);
    }
  }
  return 0;
}

4.2.3.3 进一步优化:带一点欧拉筛算法的味道

#include<stdio.h>
#include<math.h> //使用sqrt开平方根函数一定要记得引入math库
int main() {
  int i, j;
  int isPrimerNumber;
  // 2 一定是素数,提前打印输出
    printf("2\n");
  // i = i + 2 保证了 i 一定是奇数
  for (i = 3; i < 1000; i = i + 2)//也可以写 i += 2 [复合赋值运算符]
  {
    //默认1即true,表示为素数
    isPrimerNumber = 1;
    /*
      1.对每个 i 进行判断是否为素数
      2.循环变量初始化
        j = 3:从3开始取模,为什么不为1和2
        - j=1,则肯定能取模为0,且不管是否为素数,肯定能被1和自身整除
        - j=2,我们已经在外层循环筛选出 i 肯定不为偶数因此没必要
      3.循环变量迭代(这里其实就有了一种名为 欧拉筛 算法的思想了)
        j += 2:这导致了j一定为奇数,那为什么不考虑j为偶数的情况呢,
            你想,如果 j 可以是偶数且能被 i 整除,那么 i 一定2的倍数
            我们在外层循环又强调了 i 肯定是奇数,那是不是j为偶数没有意义了。
    */
    for (j = 3; j <= sqrt(i); j += 2)
    {
      if (i % j == 0)// 取模运算符:% 
      {
        //如果 i 能对 j 取模的结果为0,即说明j能被i整除,标记一下false,为非素数
        isPrimerNumber = 0;
        //退出内层循环,因为已经判断出了此时 i 不为素数,也没有继续循环下去的必要了
        break;
      }
    }
    if (isPrimerNumber)
    {
      //则说明 i 是素数
      printf("%d\n", i);
    }
  }
  return 0;
}

4.2.4 拓展方案:欧拉筛

同样的,这个欧拉筛算法的知识点我们放在数组来讲,是一种进一步优化求素数的算法,这里先做个大概了解。感兴趣的可以先去了解,还是把代码放下面,现在不做讲解。

#include<stdio.h>
int judge[10000]; //保存   这个数是否为素数   
int prime[10000];    //用来保存已经找到的素数 
int count;   //统计素数的个数
int main()
{
  int n;
  scanf_s("%d",&n);
  for(int i = 2;i <= n;i++)
  {
    if(!judge[i]) //如果judge[i] 没有被标记为合数 
    {
      prime[count] = i;     //将 i 保存进数组
      count++;
    } //i*prime[j] 为合数
    for(int j = 0;j < count && i*prime[j] <= n;j++) //j 遍历的 是 已保存 的 素数    
    {       
      judge[i*prime[j]] = 1;   // 将 i*prime[j] 进行标记为合数
        if(i % prime[j] == 0)//如果  prime[j]  是 i 的最小质因子,跳出
                break;      
    }
  } 
  printf("%d\n",count);
}

4.3 核实登录

4.3.1 题目描述

模拟登录while+switch:输入1登陆成功,其他都登陆失败,需要重新登录,直到登陆成功

4.3.2 代码

#include<stdio.h>
int main()
{
    int n;
    while (1)
    {
        scanf_s("%d", &n);
        switch (n) {
        case 1:
            printf("登陆成功\n");
            return 0;
        default:
            printf("登陆失败\n");
        }
    }
    return 0;
}

4.4 鸡兔同笼

4.4.1 题目描述

鸡兔同一个笼子中,已知头共 n 个,脚共 m 个,问有鸡兔各多少只,请用循环来解决该问题

4.4.2 思路分析

  • 假设鸡有0只,那么兔子有(n-0)只,当满足 0 * 2 + n * 4 == m(脚的总个数) 时,说明我们假设成立,不用再向下假设,此题得解。
  • 假设鸡有1只,那么兔子有(n-1)只,当满足 1 * 2 + (n-1) * 4 == m(脚的总个数) 时,说明我们假设成立,不用再向下假设,此题得解。
  • 假设鸡有2只,那么兔子有(n-2)只,当满足 2 * 2 + (n-2) * 4 == m(脚的总个数) 时,说明我们假设成立,不用再向下假设,此题得解。

很明显,这样的假设顺序我们可以用循环来实现。

4.4.3 代码

#include<stdio.h>
int main()
{
    //设置鸡共 i 只,兔共 j 只
    int i, j;
    //设置头共n个,脚共m个
    int n, m;
    scanf_s("%d %d", &n, &m);
    for (i = 0; i <= n; i++)
    {
        //鸡 i 只,又已知头为 n 个,则兔子(n-i)个
        j = n - i;
        if (2 * i + 4 * j == m) {
            printf("鸡 %d 个,兔 %d 个", i, j);
            return 0;
        }
    }
    printf("此题无解!");
    return 0;
}

4.4.3 一些说明

很显然,如果用循环来做很复杂,因为设鸡有a只,兔有b只,则 a + b = n,2a + 4b = m,联解得 a = (4n-m)/2b = n-a。输入了n和m,a和b我们可以直接求得,不需要循环,但这里只是一个对循环操作的练习,不用太在意。

4.5 斐波那契数列

4.5.1 题目描述

斐波那契数列(Fibonacci sequence),又称黄金分割数列,因数学家莱昂纳多·斐波那契(Leonardo Fibonacci)以兔子繁殖为例子而引入,故又称为“兔子数列”,指的是这样一个数列:1、1、2、3、5、8、13、21、34、……

求斐波拉契数列第15个值。

4.5.2 思路

在数学上,斐波那契数列以如下被以递推的方法定义:F(0)=0,F(1)=1, F(n)=F(n - 1)+F(n - 2)(n ≥ 2,n ∈ N*)。这也很容易转换为代码。

4.5.3 代码

#include<stdio.h>
int main()
{
  int first = 1;//第一项
  int second = 1;//第二项
  int three;
  for (int i = 0; i < 13; i++)
  {
    /*
    记住:每次都是利用前两个数来求第三个数,
          求完后前两个数都往后移动一位。
    1.第一次循环:
      - three = 2
      - first = second  =》 1
      - second = three  =》 2
    2.第二次循环:
      - three = 3
      - first = second  =》 2
      - second = three  =》 3
    3.第三次循环:
      - three = 5
      - first = second  =》 3
      - second = three  =》 5
    */
    three = first + second; //第三项
    first = second; //将第二项的值赋给第一项
    second = three; //将第三项的值赋给第二项
  }
  printf("%d", three);
    return 0;
}

4.5.4 拓展方案:函数递归

#include <stdio.h>
int fib(int input){
  if (input <= 2) {
    return 1;//前两项都为1,所以直接返回1即可
  }
  return fib(input - 1) + fib(input - 2);
}
void main() {
  int input = 0;
  scanf("%d",&input);
  printf("第%d位斐波那契数为:%d",input,fib(input));
}

4.6 打印三角形

4.6.1 题目描述

请使用循环打印出如下三角形:

4.6.2 思路分析

打印出一个占四行的三角形,它的每一行前面会有空格,因此首先要算出空格多少的规律,以及每行的数目。设行数为 n ,在这里 n 就为4。

  • 空格计算:
  1. 第一行:空格数量为 n - 1,即3
  2. 第二行:空格数量为 n - 2,即2
  3. 第 i 行:空格数量为 n - i,因此进行 (n - i)printf(" "); 打印空格的操作
  • 星星计算:
  1. 第一行:星星数量为 2 * 1 - 1,即1
  2. 第二行:星星数量为 2 * 2 - 1,即3
  3. 第三行:星星数量为 2 * 3 - 1,即5
  4. 第 i 行:星星数量为 2 * i - 1,因此进行 (2 * i - 1)printf("*"); 打印星星的操作

实际上,你从我的上述可以看出,这就是一个找数学规律的过程,然后我们把找到的规律转变为代码就行了。

4.6.3 代码

#include<stdio.h>
int main()
{
  int n = 4;//表示三角形的总行数
  int i, j;
  for (i = 1; i <= n; i++)
  {
    //打印空格 n- i 次
    for (j = 0; j < n-i; j++)
    {
      printf(" ");
    }
        //打印星星 2*i-1 次
    for (j = 0;j < 2*i-1; j++)
    {
      printf("*");
    }
        //打完一行就换行,接着下次循环即打印下一行
    printf("\n");
  }
    return 0;
}

4.6.4 拓展训练:打印菱形

💬 其实很简单,既然能够打印正三角形,而菱形又是由正三角形和倒三角形组成的,所以菱形应该很容易打印。自己先尝试一下吧。

#include<stdio.h>
int main()
{
  int n = 4;//表示三角形的总行数
  int i, j;
  //打印正三角形
  for (i = 1; i <= n; i++)
  {
    //打印空格 n- i 次
    for (j = 0; j < n - i; j++)
    {
      printf(" ");
    }
    //打印星星 2*i-1 次
    for (j = 0; j < 2 * i - 1; j++)
    {
      printf("*");
    }
    //打完一行就换行,接着下次循环即打印下一行
    printf("\n");
  }
  //打印倒三角形
  for (i = n - 1; i >= 1; i--)
  {   
    //打印空格 n- i 次
    for (j = 0; j < n-i; j++)
    {
      printf(" ");
    }
    //打印星星 2*i-1 次
    for (j = 0; j < 2 * i - 1; j++)
    {
      printf("*");
    }
    //打完一行就换行,接着下次循环即打印下一行
    printf("\n");
  }
  return 0;
}

🔖 小结

💬 本章我们了解了三大基本结构:顺序、分支、循环,第四节练习题很有必要做一做,遇到任何疑惑联系软件协会

🏠 了解更多关注软协官网:https://www.csuftsap.cn/

💚 来自软件协会编辑,注册会员即可获取全部开源.md资源,请勿转载,归软件协会所有。

相关文章
|
网络协议 编译器 Linux
【C语言】结构体内存对齐:热门面试话题
【C语言】结构体内存对齐:热门面试话题
418 0
|
9月前
|
存储 安全 C语言
【C语言程序设计——选择结构程序设计】预测你的身高(头歌实践教学平台习题)【合集】
分支的语句,这可能不是预期的行为,这种现象被称为“case穿透”,在某些特定情况下可以利用这一特性来简化代码,但在大多数情况下,需要谨慎使用。编写一个程序,该程序需输入个人数据,进而预测其成年后的身高。根据提示,在右侧编辑器补充代码,计算并输出最终预测的身高。分支下的语句,提示用户输入无效。常量的值必须是唯一的,且在同一个。语句的作用至关重要,如果遗漏。开始你的任务吧,祝你成功!,程序将会继续执行下一个。常量都不匹配,就会执行。来确保程序的正确性。
256 10
|
9月前
|
小程序 C语言
【C语言程序设计——基础】顺序结构程序设计(头歌实践教学平台习题)【合集】
目录 任务描述 相关知识 编程要求 测试说明 我的通关代码: 测试结果: 任务描述 相关知识 编程编写一个程序,从键盘输入3个变量的值,例如a=5,b=6,c=7,然后将3个变量的值进行交换,使得a=6,b=7,c=5。面积=sqrt(s(s−a)(s−b)(s−c)),s=(a+b+c)/2。使用输入函数获取半径,格式指示符与数据类型一致,实验一下,不一致会如何。根据提示,在右侧编辑器补充代码,计算并输出圆的周长和面积。
192 10
|
9月前
|
存储 编译器 C语言
【C语言程序设计——选择结构程序设计】求一元二次方程的根(头歌实践教学平台习题)【合集】
本任务要求根据求根公式计算并输出一元二次方程的两个实根,精确到小数点后两位。若方程无实根,则输出提示信息。主要内容包括: - **任务描述**:使用求根公式计算一元二次方程的实根。 - **相关知识**:掌握 `sqrt()` 函数的基本使用方法,判断方程是否有实根。 - **编程要求**:根据输入的系数,计算并输出方程的根或提示无实根。 - **测试说明**:提供两组测试数据及预期输出,确保代码正确性。 - **通关代码**:包含完整的 C 语言代码示例,实现上述功能。 通过本任务,你将学会如何处理一元二次方程的求解问题,并熟悉 `sqrt()` 函数的使用。
154 5
|
9月前
|
存储 算法 安全
【C语言程序设计——选择结构程序设计】按从小到大排序三个数(头歌实践教学平台习题)【合集】
本任务要求从键盘输入三个数,并按从小到大的顺序排序后输出。主要内容包括: - **任务描述**:实现三个数的排序并输出。 - **编程要求**:根据提示在编辑器中补充代码。 - **相关知识**: - 选择结构(if、if-else、switch) - 主要语句类型(条件语句) - 比较操作与交换操作 - **测试说明**:提供两组测试数据及预期输出。 - **通关代码**:完整代码示例。 - **测试结果**:展示测试通过的结果。 通过本任务,你将掌握基本的选择结构和排序算法的应用。祝你成功!
123 4
|
11月前
|
存储 搜索推荐 算法
【数据结构】树型结构详解 + 堆的实现(c语言)(附源码)
本文介绍了树和二叉树的基本概念及结构,重点讲解了堆这一重要的数据结构。堆是一种特殊的完全二叉树,常用于实现优先队列和高效的排序算法(如堆排序)。文章详细描述了堆的性质、存储方式及其实现方法,包括插入、删除和取堆顶数据等操作的具体实现。通过这些内容,读者可以全面了解堆的原理和应用。
406 16
|
9月前
|
存储 算法 安全
【C语言程序设计——选择结构程序设计】求阶跃函数的值(头歌实践教学平台习题)【合集】
本任务要求输入x的值,计算并输出特定阶跃函数的结果。主要内容包括: 1. **选择结构基本概念**:介绍if、if-else、switch语句。 2. **主要语句类型**:详细解释if、if-else、switch语句的使用方法。 3. **跃迁函数中变量的取值范围**:说明如何根据条件判断变量范围。 4. **计算阶跃函数的值**:通过示例展示如何根据给定条件计算函数值。 编程要求:在右侧编辑器Begin-End之间补充代码,实现阶跃函数的计算和输出。测试说明提供了多个输入及其预期输出,确保代码正确性。最后提供通关代码和测试结果,帮助理解整个过程。
130 0
|
9月前
|
存储 算法 安全
【C语言程序设计——选择结构程序设计】判断一个数是不是5和7的倍数(头歌实践教学平台习题)【合集】
本任务要求输入一个正整数,判断其是否同时是5和7的倍数,若是输出&quot;Yes&quot;,否则输出&quot;No&quot;。内容涵盖选择结构的基本概念、主要语句类型(if、if-else、switch)及条件判断逻辑,帮助理解编程中的分支执行与条件表达式。测试用例包括正数、负数及非倍数情况,确保代码逻辑严谨。通关代码示例如下: ```cpp #include &quot;stdio.h&quot; int main(){ int a; scanf(&quot;%d&quot;, &a); if (a &lt;= 0){ printf(&quo
264 0
|
9月前
|
编译器 C语言 C++
【C语言程序设计——选择结构程序设计】求输入的日期是该年的第几天(头歌实践教学平台习题)【合集】
本任务要求编写程序,根据用户输入的年月日(以空格或回车分隔),计算并输出该天是该年的第几天,需注意判断闰年。主要内容包括: 1. **任务描述**:实现从键盘输入年月日,计算该天是当年的第几天。 2. **相关知识**: - `switch` 结构的基本语法及使用注意事项。 - 判断闰年的条件:能被4整除但不能被100整除,或能被400整除的年份为闰年。 3. **编程要求**:根据提示补充代码,确保程序正确处理输入并输出结果。 4. **测试说 示例代码展示了如何使用 `switch` 语句和闰年判断逻辑来完成任务。通过此练习,掌握 `switch` 语句的应用及闰年判断方法。
261 0
|
12月前
|
编译器 C语言 Python
C语言结构
C语言结构
63 0