假设我们要查找一串数字,这串数字有16个数:1-16。怎么查找能找到自己想要的数字呢?
我们首先可以尝试遍历算法:
#include <stdio.h>
int main()
{
int arr[16] = { 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16 };
int sz = sizeof(arr) / sizeof(arr[0]);
//sizeof数组名表示整个数组的大小,
//除以数组第一个元素的大小得到的就是数组的长度。
int i = 0;
int n = 0;//n是我们要输入想要查找的数字
scanf("%d", &n);
for (i = 0; i < sz-1; i++)
//因为数组下标是从0开始的,所以数组中最大的下标应该是最大值-1
{
if (arr[i] == n)
{
printf("找到了这个数字的下标是:%d\n", i);
break;
}
}
if (i >= 16)
{
printf("数组中没有这个数字\n");
}
return 0;
}
运行结果如下:
但是这是数字少,如果数字大呢?1W个数字,100W个数字呢?程序需要运行很长时间。
所以我们采用新的方法——折半查找(二分查找)
折半查找的逻辑:每次都去找数组中元素的中间的哪个元素,找到后去和要查找的数字相比,如果比要找的数字小,就往后移动一下,逐渐缩小范围。
代码如下:
#include <stdio.h>
int main()
{
int arr[16] = { 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16 };
int sz = sizeof(arr) / sizeof(arr[0]);
//sizeof数组名表示整个数组的大小,
//除以数组第一个元素的大小得到的就是数组的长度。
int left = 0;//left存放的是数组左边最小值的下标
int right = sz - 1;//right存放的是数组右边最大值的下标
int mid = (left + right) / 2;
//mid存放的是数组中间的那个数的下标。
int n = 0;
scanf("%d", &n);
while (left <= right)
{
if (n > arr[mid])
{
mid = left++;
}
else if (n < arr[mid])
{
mid = right--;
}
else
{
printf("找到了,数字下表是:%d\n", mid);
break;
}
}
if (left > right)
{
printf("数组中没有这个数字\n");
}
return 0;
}
上述代码的逻辑是:
1、声明个数组,定义个存储数组长度的变量sz,sizeof数组名表示整个数组的大小, 除以数组第一个元素的大小得到的就是数组的长度。数组长度sz-1就是数组最大下标的值。
2、然后,定义了三个变量分别是left、right、mid,left存放的是左边数组的下标从下标0开始,right存放的是右边数组的下标,从下标sz-1开始。mid存放的是数组中间数字的下标。
3、然后进入循环体,只有当left<=right的时候,说明这个变量一个在左一个在右,最多两个下标重合,但是如果left都大于right了,说明数组中已经找不到这个元素了。
4、进行判断,arr[mid]代表的就是数组元素,当arr[mid]<n的时候,说明要查找的元素在这个中间元素的右边,所以left++后再赋值给mid,如果,arr[mid]>n,说明要查找的元素在中间元素的左边,所以right--后再赋值给mid。这样一次次的找下去。
5、如果,left>right了,说明数组中没有这个数字。
6、这里需要注意,因为int类型的大小是有上限的,如果,left+right的值超过了这个上限,结果就会有问题。 比如,有两个不一样长的木棍,让我们把他俩变得一样长,是不是可以这样,把长的那根木棍找到和短的木棍一样长的位置做个标记,把剩余的取一半加到短的木棍上去。
7、所以我们可以采用下面的方法:mid = left + (right - left) / 2;