判断和循环——实战收尾篇1(二分法、抛硬币等)

目录

​1.独领风骚的表达式

2.求n的阶乘: 

3.求1~10的阶乘之和

4.找一个有序数组中的具体数字(二分法)

5.演示多个字符从两端,向中间汇聚

6.抛硬币游戏

补充: 随机数的设置


​1.独领风骚的表达式

 解析:

      优先计算表达式结果,即优先判断i==1的逻辑结果,结果为真,以%d格式化输出,真表现为1,输出。

      考查:表达式的优先计算

2.求n的阶乘: 

  大家现在的水平足以驾驭的代码,不作详解:

函数封装:

 

3.求1~10的阶乘之和

上一题我们写出了求n阶乘的函数,求阶乘之和不过是把每次求的阶乘相加即可

解析: 

       因为我们已经写出求n的阶乘的函数,要求几个阶乘的和,无非是把阶乘函数多次调用, 把得到的几个数相加求和即可。

       那么现在要处理的问题是,怎样重复调用函数呢?很显然,我们需要一个循环来对函数进行多次调用。那调用之后,怎么求和呢?我们补充的函数 AddFact 就是对函数调用并求和的功能实现。

       输入一个n值,用来表示计算由1到n的阶乘和,当然,你可以把for循环里的条件进行更改,来实现你想要的一些数的阶乘和。 进入for循环后,执行语句,将num和MyFact(i)的值赋给num,既然要MyFact的返回值,自然要先调用该函数,因为i第一次进入循环为1,所以调用MyFact(1),返回的是1的阶乘,因此num=1!(因为num初始化为0,直接省略),下一次循环i变为2,调用MyFact(2),返回值为2的阶乘,所以把第一次赋值后的num和2的阶乘赋给num,此时num=1!+2!,以此类推,最后我们就会求出1~10的阶乘和。

       第一个函数只负责求n的阶乘,第二个函数负责进一步处理,我们把这种处理方式叫作程序模块化(方便后期修改维护),要使用某种功能,就要定义函数来实现该功能,而不能不定义直接使用,我们这个叫作面向过程。

      如果我们想要更改功能,如求几个数的阶乘差,我们只需要把第二个函数里改成减法即可。

    

4.找一个有序数组中的具体数字(二分法)

       如在该数组中找数字9是否存在,很显然我们能看到9确实存在,但如果数组里面有一万个数呢,有一千万个数呢,我们是否能用眼睛看出来(头铁的兄弟请放下爪爪(手动滑稽)),但计算机很容易就能找出来。那怎样实现这个程序呢?

       第一种方法(枚举法):我们可以暴力的进行枚举,直接把所要找的数字与数组里的数进行一一比对,有就是有,没有就是没有,但很显然,这种方法相对效率低下,毕竟可能你要找的那个数字是第一千万个元素,我们在这里讲解第二种方法。

       第二种方法(折半查找法/二分查找法):我们已经规定了该数组是有序数组,而我们此时只进行有序数组的讨论,在这里我们以升序为例进行讨论。

       比如我们要找数字9,那我们直接让9跟该数组的中间元素(或稍有偏差,偶数数组里一般偏左或偏右一个元素),如果9比中间元素小,因为为升序排列,所以中间元素肯定比中间元素后面所有的元素都小,而9比中间元素还小,说明9这个数字肯定不在数组的后一半中,所以要么就在数组前一半中,要么不存在,因此我们下次寻找只需要在该数组的前一半元素中进行查找,直接把后一半扔掉即可,大大提高了效率,依次类推,我们只需要与剩下的数中的中间元素比较即可,我们把这个叫作二分法。

实现如下图(内有详细注释讲解)

//用二分法找一个有序数组中的具体数字
#pragma warning(disable:4996)
#include<stdio.h>
int Find(int arr[], int who, int size)//我们需要知道查找的数字,和原来的数组,以及数组的大小
{
    int left = 0;//left 定义为查找范围的最左下标,即第0个元素的下标
    int right = size - 1;//right 定义为查找范围的最右下标,即最后一个元素的下标
    //因为第一个元素的下标是0,所以第最后一个元素的下标是size-1.
    while (left <= right)//等left==right时,说明查找范围只剩下一个数,该数可能为要查找的数,所以可以取等,若该数仍然不是要查找的数,说明该数组已经查无此数
    {
        int mid = (left + right) / 2;//因为我们需要使用中间元素的下标,所以在此定义
        if (who > arr[mid])
        {
            //要查找的元素大于中间元素,只需要查找数组的右一半即可
            left = mid;//赋值中间元素的下标给left,即下一次查找的最左边范围从mid开始。
            left++;//因为我们已经比较了who和mid,所以我们不再需要从mid开始,因此从mid+1个元素开始
        }
        else if (who < arr[mid])
        {
            //要查找的元素小于中间元素,只需要查找数组的左一半即可
            right = mid;//赋值中间元素的下标给left,即下一次查找的最右边范围从mid开始。
            right--;//因为我们已经比较了who和mid,所以我们不再需要从mid开始,因此从mid-1个元素开始
        }
        else//说明who==arr[mid],即已经找到该数
        {
            return mid;//返回该元素下标
        }
    }
    return -1;//从while循环出来说明数组中查无次数,返回负值
}
int main()
{
    int arr[10] = { 1,5,7,9,15,25,43,56,65,70 };//升序数组
    int size = sizeof(arr) / sizeof(arr[0]);//计算数组所占总空间
    int who;//who 是我们想要查找的数
    scanf("%d", &who);
    int sub = Find(arr, who, size);//Find 函数即为实现二分查找功能的函数
    //sub 是该函数的返回值,返回值为下标
    if (sub >= 0)//数组返回值为非负数,则说明存在该数字
    {
        printf("存在该数字,该数字在数组中的下标是 %dn", sub);
    }
    else//返回值为负数,说明该数字不存在
    {
        printf("不存在该数字n");
    }
    return 0;
}

(注意代码块是可以滑动的哦,别以为看不到了,hhh)

注意:

      Int类型的除法,在定义mid时,我们使用 int mid=(left+right)/2 ,在操作符篇讲过‘/’操作符,为零项取整原则,即8/2=4,7/2=3,,-7/2=-3.

5.演示多个字符从两端,向中间汇聚

如hello的实例演示     

        h####o

        he##lo

         hello

实现如下图:

//演示多个字符从两端,向中间汇聚

#pragma warning(disable:4996)

#include<stdio.h>

#include<windows.h>

int main()

{

//首先需要一个字符串

char arr1[] = "hello world! nice to meet you!!";

char arr2[] = "###############################";

//因为是从两端向中间汇聚,所以我们需要两端下标

int left = 0;

int right = strlen(arr2) - 1;

   //strlen 计算字符串长度,所以strlen-1是最后一个元素的下标

while(left <= right)

{

arr2[left] = arr1[left];

arr2[right] = arr1[right];

printf("%sr", arr2);

left++;

right--;

Sleep(1000);

}

return 0;

}



注意:

1.  这里用strlen求字符串长度。但用sizeof求开辟空间也是可以的,只不过因为字符串的结束标志默认是’/0’,sizeof计算的大小也包括了’/0’的大小,因此sizeof计算的空间大小要比strlen计算的长度多1,使用时要-2才能做下标

2.  ‘/r’我们在转义中说过,即光标回到该行的开头,如果继续输出,则会覆盖之前的内容,由于每次输出字数相同,所以该代码里每次都会重新覆盖。

3.Sleep函数(s是大写)需要引用头文件window.s ,作用是停顿,停顿单位是毫秒(1秒=1000毫秒)

6.抛硬币游戏

//抛硬币游戏

#include<stdio.h>

#include<stdlib.h>

#include<time.h>

int main()

{

srand((unsigned long)time(NULL));

//用来设置随机数

int a[2] = { 0 };

//创建含两个int类型元素的数组,数组中的两个元素分别代表正面的次数和反面的次数

for (int i = 0; i < 100; i++)//随机投掷的次数

{

a[rand () % 2]++;

//一个随机数余2所得必然是0或1,则该行代码意思是数组中随机一个元素+1,即正反面随机+1

}

printf("Up : %dnDown : %dn", a[0], a[1]);

//Up指正面;Down指反面

return 0;

}

补充: 随机数的设置

 随机数的设置函数是rand,需要的头文件是stdlib.h,作用是生成一个随机数,但该随机数的生成是有顺序的,rand随机数的生成会首先固定一个随机数的起点(称为种子)(每一个数都代表着一个随机数的顺序),继而生成随机数,该起点不变时,生成的随机数顺序也不变。

   上面这是第一次运行结果,现在我重新运行之后:

 

      看!

      两次运行随机数都一样,因此这并不是我们真正要的随机数。

 (注意,该解析有基础者可分析,小白莫在意,记住随机值的写法即可)

        要想随机值不断变化,我们就要让他随机的起点(种子)不断变化,这样随机顺序也会不断变化,自然就成了真正的随机数,而用什么来作rand的种子呢?我们需要一个不断变化的东西。很显然,时间是不断变化的,因此我们引入time(NULL),time()里面本来应该放一个指针,我们不考虑其他(记住即可),滞空即可,

       而time(NULL)就是返回从1970年元旦午夜0点到现在的秒数。

          因为现在的时间不断变化,因此time(NULL)的返回值在不断变化,因此我们把time(NULL)作为rand()的种子即可,那么我们如何来设置种子呢?这里需要引出另一个函数 srand(),该函数的作用就是设置一个种子。

        但srand()里的类型要求是unsigned long int(无符号长整型)(int可省略),而time(NULL)返回值类型为长整型,类型不符合,因此我们需要用到强制类型转换

         写作(unsigned long)time(NULL),意思就是 把time()的返回值类型强制转换为unsigned long类型,因此随机值种子的设置完整就是 srand((unsigned long)time(NULL));

设置好之后,在使用rand就是真正的随机数了

随机数设置好后,我们就可以模拟硬币落下的随机正反面了

      好了,这篇就先到这里啦,实践才是编程的本质,相信如果大家明白上面几个代码原理后,一定多少有点收获(不确定的小表情)!

       而在下篇我们将讲解一个实战小项目(猜数字游戏噢

     看!编程也不是很难嘛(抱头),而且还是很有趣的,才学了这么点我们就可以写我们自己的游戏啦!下篇见噢!

      如有疑惑,评论取留言即可,我会常看的,其他人也可以帮忙解惑。大家一起加油!

本图文内容来源于网友网络收集整理提供,作为学习参考使用,版权属于原作者。
THE END
分享
二维码
< <上一篇
下一篇>>