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资源,请勿转载,归软件协会所有。

相关文章
|
1天前
|
C语言
C语言之分支结构
C语言之分支结构
23 0
|
1天前
|
C语言
C语言结构体内存对齐
C语言结构体内存对齐
|
1天前
|
Serverless C语言
C语言程序通常具有以下基本结构
C语言程序通常具有以下基本结构
11 0
|
1天前
|
C语言
C语言选择结构
C语言选择结构
17 0
|
1天前
|
存储 C语言
C语言顺序结构:基础与实践
C语言,作为一种广泛使用的编程语言,其程序结构可以分为三种基本类型:顺序结构、选择结构和循环结构。在这篇文章中,我们将重点讨论顺序结构的概念、特点以及如何在C语言中实现顺序结构的程序设计。
44 1
|
1天前
|
存储 编译器 Linux
【C语言】自定义类型:结构体深入解析(二)结构体内存对齐&&宏offsetof计算偏移量&&结构体传参
【C语言】自定义类型:结构体深入解析(二)结构体内存对齐&&宏offsetof计算偏移量&&结构体传参
|
1天前
|
C语言
C语言最基本程序控制结构
C语言最基本程序控制结构
16 0
|
1天前
|
C语言
C语言(指针详解)重点笔记:指针易错点,都是精华
C语言(指针详解)重点笔记:指针易错点,都是精华
5 0
|
1天前
|
C语言
【精通C语言】:分支结构if语句的灵活运用
【精通C语言】:分支结构if语句的灵活运用
25 1
|
1天前
|
编译器 Linux C语言
C语言:结构体(自定义类型)知识点(包括结构体内存对齐的热门知识点)
C语言:结构体(自定义类型)知识点(包括结构体内存对齐的热门知识点)

热门文章

最新文章