大家好,很高兴又和各位见面了!!!今天我们继续分享分支与循环的相关习题,今天要分享的题目如下所示:
- 在一个有序数组中查找具体的某个数字n。
今天的题目感觉有点难以理解,没关系,接下来我就来给各位分享这些题目的解析,希望各位看完解析后,能够自己动手实操一遍,加深对这些内容的理解。
习题演练——分支与循环篇
在一个有序数组中查找具体的某个数字n
经过前面两个篇章的习题演练,我相信大家对我的解题流程比较熟悉了,这里我就不多强调了。下面我们开始对这一题进行分析吧;
题目解析
因为阅读这篇文章的朋友有刚接触C语言的,也有基本功已经很扎实了的,所以这里我就简单介绍一下数组。
数组
所谓的数组,我们可以简单的理解为是一个集合,一个数组由数组元素类型+数组名+数组大小组成,如下所示:
//数组
int arr[10];
//int——数组元素类型;
//arr——数组名,这个名称凭自己的喜好随意定义,没有特别的要求;
//[10]——[数组大小],数组的大小是由中括号加具体数值组成;
数组下标
数组内的元素都是有对应下标的,这里我们先记结论:
- 数组元素的下标从0开始依次增加,如例子中的arr数组,它里面有10个元素,那这十个元素的下标就是,0、1、2、3、4、5、6、7、8、9;
- 每一个下标对应一个元素,我们可以通过数组名+数组下标来访问数组内的元素,如:arr[0]、arr[1]、arr[2]、arr[3]、……、arr[9];
- 数组所占空间大小=数组元素所占空间大小*数组元素数量;
- 数组元素所占空间大小=数组元素类型所占空间大小;
- 最后一位元素下标比数组大小小1;
这里简单举一个例子:
//数组
int main()
{
int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
//int——数组元素类型;
//arr——数组名,这个名称凭自己的喜好随意定义,没有特别的要求;
//[10]——[数组大小],数组的大小是由中括号加具体数值组成;
//{ 1,2,3,4,5,6,7,8,9,10 }——数组元素,需要用大括号圈起来;
int i = 0;//定义下标变量
for (i = 0; i < 10; i++)
{
//通过数组下标访问数组元素
printf("arr[%d] = %d\n", i, arr[i]);
//arr[i]——数组元素
}
//计算数组所占空间大小
printf("%d\n", sizeof(arr));
//计算数组元素所占空间大小(以第一个元素为例)
printf("%d\n", sizeof(arr[0]));
//计算元素类型所占空间大小
printf("%d\n", sizeof(int));
//定义数组大小变量
int sz = sizeof(arr) / sizeof(arr[0]);
printf("%d\n", sz);
//sizeof——计算操作对象所占计算机内存大小关键字;
//arr[0]——数组首元素;
return 0;
}
现在我们看一下这个代码运行的结果,我们通过VS的调试界面来说明:
从运行结果中我们进一步证明了一下结论:
- 数组元素可以通过下标进行访问;
- 数组元素所占空间大小=元素类型所占空间大小;
- 数组所占空间大小=数组元素所占空间大小*数组大小;
- 最后一位元素下标比数组大小小1;
想必刚刚接触C语言的朋友们对数组应该有了个初步的认识了,后面我会将数组的内容单独作为一个篇章的内容来给各位介绍的,记得关注哦!
数组的相关内容咱们了解后,现在再回到咱们的题目中来;
题目分析
题目要求是在一个有序数组中查找具体的某个数字n。从这个题目中我们提取出关键信息:
一个有序数组、查找、数字n。
通过这三个关键信息,我们不难联想到这一题我们需要完成三个任务:
- 定义一个有序数组;
- 代码有在数组中查找的功能;
- 数组内的元素是数字;
这三个任务我们再整合一下就会变成:
- 定义一个整型/浮点型数组;
- 代码有在数组中查找的功能;
这样咱们的思路就很清晰了,这一题我们简单点,直接定义整型数组来进行解题,下面我们来分步完成;
第一步:定义一个整型数组
整型数组还是很好定义的,咱们在介绍数组时的举例就是一个整型数组,但是题目需要的是一个有序数组,那什么又是有序数组呢?有序数组和数组有什么区别?
我对这个问题的理解是,有序数组是数组的一种特殊形式,如下图所示:
从图中我们可以看到,所谓的有序就是数组里的元素按照特定的次序进行排列,如arr2的从小到大排列,arr3中的从大到小排列。这里我们直接记结论:
有序数组就是数组里的元素按照一定次序排列的数组;
在这题中我们只需要使用数组arr2或者arr3就可以了,简单点,咱们使用从小到大排列的有序数组arr2。
数组定义好后,接下来就是查找功能的实现了;
第二步:查找数字n
看到这个功能,马上就有朋友想到了,那不就是很简单,我一个一个查找过去就好了,把数组中的每一个元素都与目标数字比较一下,如果相等我们不就找到了吗?
在实现这个功能的编写之前,我们要考虑到查找的过程中会出现两种情况:
- 数字n在数组内;
- 数字n不在数组内;
为了分辨这两种情况,我们这个功能中就需要有条件语句来进行判断:
当元素在数组中找到时,元素的下标肯定小于元素个数,或者说小于数组大小;
当元素下标大于数组大小时,说明所有元素都被找过了,数组中并没有该元素。
下面我们进行编码,并用数组arr1和arr2分别测试一下:
从测试结果中我们可以看到,如果一个一个去进行查找的话,完全是可以实现的,并且数组也不需要有序;
通过这种方式实现查找,我们可以看到循环执行的次数,在arr1中执行了3次,在arr2中执行了6次。
下面我们来思考一下,如果一个数组里有100个元素、1000个元素、10000个元素甚至更多个元素,这种查找方式会是怎样的一个效果?
此时如果是无序数组的话,我们像这样查找,就凭运气了,运气好,很快就能找到,运气不好就另说了。
此时如果是一个有10000个元素的有序数组并且元素是从小到大排列,元素的值为0~10000,我要在这些元素里面找到值为5000的这个元素,此时程序需要执行5000次才能找到,这样效率是不是太低了。
那对于有序数组来说,有没有什么更高效的方法呢?
当然有,这就是我们今天要介绍的折半查找法,也叫二分法;
折半查找算法(二分法)
什么是二分法?
二分法字面意思理解就是二分,平均分的意思,通过平均分的方式将查找范围缩小,以此来提高查找效率。
下面我还是通过找5000的例子,如果我们通过二分查找法是不是只需要通过找0~10000的中间数,再将这个数与5000进行对比来确定下一次查找的范围,0~10000的平均数刚好就是5000,也就是说,如果用二分查找法,我们只需要一次就能找到。
这个例子只能说明二分法的效率高,但是并不能很好的说明二分法的工作原理,下面我们就来详细介绍一下二分法的查找逻辑:
二分法的查找逻辑
我们介绍查找逻辑还是通过图解的方式来进行介绍:
从图中我们可以看到,二分查找就是通过中间值作为一个向标,来逐步确定目标的具体范围;
每做一次查找,就会有一半的数被筛选掉,这样大大提高了我们的查找效率;
二分法筛选的方式就是通过中间值与查找对象进行对比:
- 查找对象<中间值时,则将中间值到最右边的所有对象全部排除掉,新的查找范围是从最左边到原中间值左边相邻的值;
- 查找对象>中间值时,则将最左边的值到中间值的所有对象全部排除掉,新的查找范围是从原中间值右边相邻的值到最右边;
- 查找对象=中间值时,则我们找到了目标值;
- 图中未展示的一种情况是,当所有的数都被排查掉时,那说明我们要查找的数并不在我们的范围内。
我相信看到这里,大家应该都能理解二分法查找了,接下来我们就可以开始通过二分法来实现在有序数组中查找数字了;
代码实现
从二分法的查找逻辑我们知道了在使用二分法时我们需要4个对象——查找目标、最左边的值、中间值、最右边的值,下面我以从小到大的顺序数组为例来编写代码:
第一步:定义对象
我们需要先定义数组、查找目标、最左边的值、中间值、最右边的值、下标这些内容。
第一次二分查找时,数组内的最左边的值就是首元素,最右边的值就是最后一位元素,中间值就是数组最中间的值,对应的元素下标=(左边元素的下标+右边元素的下标)/2;
下面我们开始编写第一步的代码:
第二步:查找目标
对比的方式我们还是通过条件语句来进行判断,如果中间下标的元素与目标相同,则找到目标,不相同则没有找到:
既然没有找到,我们就需要继续查找,并且改变查找范围内的端点值,这是一个重复的过程,所以我们需要使用循环语句,等到找到目标时,结束循环:
找到目标的情况我们就完美实现了,下面我们来思考一下如果没有找到目标会是什么情况?
第三步:未找到目标
这里我们还是借助图来理解:
从图中说明我们能的到结论,当在二分查找时出现左坐标大于右坐标时,说明范围内没有符合条件的数,因此判断未找到的条件就是左坐标是否大于右坐标,根据这个条件,我们来进行编码:
//习题演练——分支与循环篇
//1.在一个有序数组中查找具体的某个数字n——二分法
int main()
{
//定义顺序数组
int arr[10] = { 0,1,2,3,4,5,6,7,8,9 };
//定义查找对象
int k = 0;
scanf("%d", &k);//通过scanf函数输入查找对象
//定义下标变量
int i = 0;
//定义左值下标
int left = 0;
//定义中间值元素下标
int mid = 0;
//定义右值下标
int right = sizeof(arr) / sizeof(arr[0]) - 1;
for (i = 0; i < (sizeof(arr) / sizeof(arr[0]));i++)
{
//定义中间值元素下标
int mid = (left + right) / 2;
//中间元素与目标对比
//中间元素大于目标值,舍弃右边
if (arr[mid] > k)
{
//右值变成中间值左边相邻的值
right = mid - 1;
}
//中间元素小于目标值,舍弃左边
else if (arr[mid] < k)
{
//左值变成中间值右边相邻的值
left = mid + 1;
}
//中间元素与目标相等
else if (arr[mid] == k)
{
printf("找到了,目标元素下标为%d\n", mid);
break;
}
}
if (left > right)
{
printf("没找到,数组里没有这个数\n");
}
return 0;
}
我们输入12来测试一下,看看能不能发现找不到目标:
可以看到能够正常运行。这样我们就通过二分法完成了这一题的解答。
结语
今天的内容到这里就结束了,希望今天的内容对大家在理解运用二分法上能有帮助,接下来我会继续给大家分享C语言的相关内容,以此来帮助大家更好的学习C语言,感谢大家的翻阅,咱们下一篇见。