一.什么是二分查找
二分查找也称折半查找,是在一组有序(升序/降序)的数据中查找
一个元素,它是一种效率较高的查找方法。
因为二分查找每次查找都可以剔除一半的查找范围,所以相比顺序查找每次一个一个元素查找,查找效率提高了很多。
二.二分查找的基本思路
- 在有序表中,每次都取中间元素作为比较对象。
- 如果中间值与目标元素相等,则查找成功,返回该元素的下标。
- 如果中间值大于目标元素,则在中间值的右半区间继续查找。
- 如果中间值小于目标元素,则在中间值的左半区间继续查找。
- 确定了该元素所在范围外的元素就不需要继续查找了,不断重复以上过程,直至找到目标元素。
三.图解例子
给定一个有序数组 arr = {1,3,5,7,9,10,12,15} 中,求数字9所在数组中的下标
二分查找过程:
(1)定义两个指针 left 和 right ;left 指向首元素的下标,right 指向最后一个元素的下标。 key 为目标元素。即:
(2)求中间元素的值mid,即:mid = left + (right - left) / 2 得到中间下标 通过中间下标可以访问到中间元素 arr[mid] 。即:
有些小伙伴会问:求中间值为什么不用 mid = (left + right) / 2 呢?
原因:如果是两个较大的值,相加超过了 int 取值范围(2147483647)就会导致溢出。
(3)使用中间值 arr[mid] 和目标值 key 对比,此时 arr[mid] < key 就证明 arr[mid] 左边的值 和 arr[mid]的值都不需要继续对比了。然后将 left 指针移动到 mid + 1 的位置,查找范围就是 [mid + 1, right] 。即:
(4)继续对比,发现 arr[mid] > key。证明 arr[mid] 的值 和 arr[mid] 右边的值都不需要对比了。就让 right 指针移动到 mid-1 的位置。即:
(5)现在 arr[mid] 和 key 相等,然后返回 mid ,查找结束。
下面我们来看一道二分查找的例题,巩固理解
题目:
在一个升序数组中查找指定的数值,找到了就返回下标,找不到就返回-1.
示例一:
输入 : arr = { -1,2,3,4,5,6,7,8,9}, key = 6
输出: 5
解释:6出现在arr中并且下标为5.
示例二:
输入:arr = { -1,2,3,4,5,6,7,8,9}, key = 1
输出:-1
解释:1不存在arr中,所以返回-1
第一步
首先是在一个有序的数组中查找某个元素的,那就需要一个数组来存放一些 int 类型的数据,可以通过题目看到除了输入一个数组 arr
外,还需输入一个目标值 key,还有数组的字符串长度 len,方便我们得到 right下标
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
void main()
{
int arr[] = { -1,2,3,4,5,6,7,8,9, };
int len = sizeof(arr) / sizeof(arr[0]);
int key = 0;
scanf("%d", &key);
}
第二步
写一个二分查找函数,并且定义两个指针 left 和 right,left 为首元素的下标,right(len - 1) 为最后一个元素的下标。
然后求中间元素的下标 mid ( mid = left + (right - left) / 2) 即:
int bin_search(int arr[], int len, int key)
{
int left = 0;
int right = len - 1;
int mid = left + (right - left) / 2;
}
void main()
{
int arr[] = { -1,2,3,4,5,6,7,8,9, };
int len = sizeof(arr) / sizeof(arr[0]);
int key = 0;
scanf("%d", &key);
printf("下标:%d", bin_search(arr, len, key));
}
第三步
使用中间元素和目标值(key)做对比,假如key = 6,那么 中间元素(5) < 目标元素(6),中间元素左半部分的值及中间元素本身也就没有必要继续对比了,此时的查找范围为[mid+1,right],也就是 left = mid + 1,right位置不变,即:
如果我们查找的值是小于中间元素的时候呢?
当 目标元素 < 中间元素 时,就代表目标元素在中间元素的左半部分,右指针就需要移动,也就是right = mid - 1,left不需要移动,此时查找范围为:[left,mid-1]。
大于和小于的情况都判断了,还有相等的情况,目标元素 == 中间元素 时,就意味着找到该元素了,直接返回该下标即可。代码如下:
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
int bin_search(int arr[], int len, int key);
void main()
{
int arr[] = { -1,2,3,4,5,6,7,8,9, };
int len = sizeof(arr) / sizeof(arr[0]);
int key = 0;
scanf("%d", &key);
printf("下标:%d", bin_search(arr, len, key));
}
int bin_search(int arr[], int len, int key)
{
int left = 0;
int right = len - 1;
if (arrMid < key)
{
left = mid + 1;
}
else if (arrMid > key)
{
right = mid - 1;
}
else return mid;
}
第四步
最后重复执行以上过程,每次都让中间元素和目标元素对比,从而缩小范围,直至查找到,如果没查找到就返回-1
最后需要注意的是:
1.在查找元素时 left 和 right下标会越来越近甚至指向同一个下标,过程中 left 始终在 right 的左边(即:left <= right)。
2.如果一直找不到那个元素,两个下标必然会相互交错(即: left > right),这时循环结束。所以循环条件就是:while(left <= right)
最终代码:
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
int bin_search(int arr[], int len, int key);
void main()
{
int arr[] = { -1,2,3,4,5,6,7,8,9, };
int len = sizeof(arr) / sizeof(arr[0]);
int key = 0;
scanf("%d", &key);
printf("下标:%d", bin_search(arr, len, key));
}
int bin_search(int arr[], int len, int key)
{
int left = 0;
int right = len - 1;
while (left <= right)
{
int mid = left + (right - left) / 2;
int arrMid = arr[mid];
if (arrMid < key)
{
left = mid + 1;
}
else if (arrMid > key)
{
right = mid - 1;
}
else return mid;
}
return -1;
}
运算结果:
总结:
二分查找最重要的两个点就是区间的赋值和循环条件。
————————————————————————————————————
关于二分查找的讲解就到这里,如果哪里有问题,欢迎大家在评论区指出~~~