题目要求
在有序数组中(这里只假设只有1~10的元素),输入一个值并求它的下标是多少。
方法一:
如果使用循环一个个查找当然是可以找到,但
第一:运算量过大(因为要从第一个数字开始,所以就要查找很多次)
第二:浪费了题目有序这个条件(因为如果是无序同样也可以用这个方法)
所以,要使用更加简单的方法来解决。
就是下面这个代码
#include <stdio.h>
int main() {
int arr1[10] = { 1,2,3,4,5,6,7,8,9,10};
int i = 0,a = 0,z = 0;
scanf("%d", &a);
while (arr1[z] != a) {
z++;
}
printf("下标是%d", z);
return 0;
}
虽然这个代码看起来很简单,但是它循环了很多次。比如我有一个1~1000的数组,那个数是999,那么就要循环999次。所以,这是很笨的方法。
方法二
首先思考“有序”这个条件,“有序”说明如果我先找到一个数,如果找到的数字比需要查找的那个数小的话就表示我找到的数字前面的数字都不需要考虑了,因为我找的那个数比前面的数字都要大,所以我只需要考虑后面的数字。同理,如果比需要找的数要大的话就不用考虑后面的数。
根据思路那么就可以想出下面的方法,二分法查找法
先说一个小技巧,遇到这种需要循环的题目可以先思考第一次运行所需要的代码。
解题思路
首先,可以对最左边和最右边的下标分别设为’left’和’right’,然后再设置一个’mid’来作为中间数((left+right)/2),如图:
最上面的数字是下标,下面的数字是元素。
第一次运行就可以这么写(自行理解下),只写第一次运行的代码。
#include <stdio.h>
int main() {
int arr1[10] = { 1,2,3,4,5,6,7,8,9,10};
int left = 0;
int right = 9;
int i = 0;
scanf("%d",&i);//需要查找下标的数
int mid = (left + right) / 2;
if (arr1[mid] < i) {
left = mid + 1;
}
else if (arr1[mid] = i) {
printf("这个数的下标是%d\n", mid);
}
else {
right = mid - 1;
}
}
这里的’left = mid + 1’是因为’mid’已经小于’i’(我要查找的数),那么就不用让left == mid,而是等于mid前面的数字,因为mid已经知道是小于了,不需要再把mid算进去。同理right也是一样的。
求出第一次循环后,就可以加入循环和其他的修饰语句了。那么循环的条件是?
当lefe一直加,right一直减后,最后会出现left = right的情况,可以自己试试,或者上网搜搜关于二分法的知识。所以如果left = right的时候还是没有找到那个数的下标的话,那么说明那个数不在给的数组范围内
代码如图
代码里面都有注释,相信可以看懂,不懂就可以私信我。
对于两处地方做一个更深一点的讲解
-
求平均值
如图,对于mid的求法有两个,一个是熟悉的普通求平均法,但这个方法有个缺陷,right不能太大,如果太大就会溢出,导致得到的值不符合逻辑。
所以有第二种求平均的方法,如果看不明白可以看下面的图来理解。
所以求平均值(如果数很大的话)就可以写为(这里用left和right来打比方)
平均值 = (right - left)/2+left -
求元素个数
sizeof的用法在我以前的文章里面都讲到了,不会的话可以去看
相信不需要过多解释一看就懂,只是值得注意的就是’right = sz - 1’,因为right是等于下标(9),而这有是个元素,所以要减一。
当然,这里sizeof的求法不是很严谨,再有的地方用不了(比如求字符数组的元素个数),所以可以用strlen(它不会计算’\0’的存在,而sizeof会把’\0’也算入进去),这个方法之后的文章会讲到(不出意外就是下一篇文章)