目录
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("存在该数字,该数字在数组中的下标是 %d\n", 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("%s\r", 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 : %d\nDown : %d\n", 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就是真正的随机数了
随机数设置好后,我们就可以模拟硬币落下的随机正反面了
好了,这篇就先到这里啦,实践才是编程的本质,相信如果大家明白上面几个代码原理后,一定多少有点收获(不确定的小表情)!
而在下篇我们将讲解一个实战小项目(猜数字游戏噢)
看!编程也不是很难嘛(抱头),而且还是很有趣的,才学了这么点我们就可以写我们自己的游戏啦!下篇见噢!
如有疑惑,评论取留言即可,我会常看的,其他人也可以帮忙解惑。大家一起加油!