1.了解认识二分查找
二分查找是一种在有序数组中查找特定元素的算法。它的工作原理是将数组分成两半,然后确定目标值可能在哪一半,最终缩小搜索范围直到找到目标值或确定目标值不存在为止。
具体步骤如下:
a.初始化两个指针,一个指向数组的起始位置(通常称为左指针),一个指向数组的结束位置(通常称为右指针)。
b.计算中间位置的索引:mid = (left + right) / 2。
c.比较中间元素与目标值:
如果中间元素等于目标值,则找到了目标,返回中间元素的索引。
如果中间元素大于目标值,则目标值可能在左半部分,更新右指针:right = mid - 1。
如果中间元素小于目标值,则目标值可能在右半部分,更新左指针:left = mid + 1。
4.重复步骤 2 和 3,直到找到目标值或左指针大于右指针为止。
这种算法的时间复杂度是 O(log n),其中 n 是数组的长度。二分查找通常在有序数组中效率很高,但前提是数组必须是有序的。
2.二分查找的三种方法
a.递归法
-----------递归法是一种直观且简洁的实现方式。
在每一步中,递归函数会计算中间位置 mid,并比较中间元素与目标值。
----如果中间元素等于目标值,返回中间元素的索引。
----如果中间元素大于目标值,在左半部分递归查找。
----如果中间元素小于目标值,在右半部分递归查找。
递归的结束条件是找到目标值或左指针大于右指针。
#include <stdio.h>
// 递归实现的二分查找函数
// 参数说明:arr为有序数组,target为要查找的目标值,left和right分别为查找范围的左右边界
int binarySearchRecursive(int arr[], int target, int left, int right) {
if (left <= right) {
int mid = left + (right - left) / 2;
if (arr[mid] == target) {
return mid; // 如果中间元素等于目标值,则返回中间元素的下标
}
else if (arr[mid] > target) {
// 如果中间元素大于目标值,在左半部分递归查找
return binarySearchRecursive(arr, target, left, mid - 1);
}
else {
// 如果中间元素小于目标值,在右半部分递归查找
return binarySearchRecursive(arr, target, mid + 1, right);
}
}
else {
return -1; // 如果左指针大于右指针,说明目标值不存在,返回 -1
}
}
int main() {
int arr[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
int target = 0;
scanf_s("%d",&target);
int result = binarySearchRecursive(arr, target, 0, sizeof(arr) / sizeof(arr[0]) - 1);
if (result != -1) {
printf("找到了,下标是%d\n", result);
}
else {
printf("找不到\n");
}
return 0;
}
b.迭代法:
----- 迭代法是使用循环来实现的,通过更新左右指针来不断缩小搜索范围。
-----循环的条件是左指针小于等于右指针。
-----在每一步中,计算中间位置 mid,并比较中间元素与目标值。
-----根据比较结果更新左右指针。
#include <stdio.h>
int main()
{
int arr[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
int left = 0;
int right = sizeof(arr) / sizeof(arr[0]) - 1;
int key = 0; // 要查找的数字,从用户输入获取
scanf_s("%d", &key);
int mid = 0; // 记录中间元素的下标
int find = 0; // 记录是否找到目标值
// 使用迭代法进行二分查找
while (left <= right)
{
mid = (left + right) / 2;
if (arr[mid] > key)
{
right = mid - 1; // 如果中间元素大于目标值,在左半部分继续查找
}
else if (arr[mid] < key)
{
left = mid + 1; // 如果中间元素小于目标值,在右半部分继续查找
}
else
{
find = 1; // 如果中间元素等于目标值,标记为找到
break;
}
}
// 输出结果
if (1 == find)
printf("找到了,下标是%d\n", mid);
else
printf("找不到\n");
return 0;
}
左侧边界查找:
----有时候,二分查找并不仅仅是为了找到目标值是否存在,而是找到目标值的左侧边界(最左边的相同值的位置)。
----在比较中间元素时,如果中间元素大于等于目标值,在左半部分继续查找。
----如果中间元素小于目标值,在右半部分继续查找,但要更新左指针为 mid + 1,因为要找的是左侧边界。
左侧边界查找法的步骤如下:
1.初始化左右指针,左指针初始化为 0,右指针初始化为数组长度。
2.在每一步中,计算中间位置的索引:mid = (left + right) / 2。
3.比较中间元素与目标值:
----如果中间元素小于目标值,说明目标值可能在右半部分,更新左指针:left = mid + 1。
----如果中间元素大于等于目标值,说明目标值可能在左半部分,更新右指针:right = mid。
4.重复步骤 2 和 3,直到找到目标值的最左边位置。
#include <stdio.h>
// 左侧边界查找的函数
// 参数说明:arr为有序数组,target为要查找的目标值,size为数组的大小
int leftBoundBinarySearch(int arr[], int target, int size) {
int left = 0;
int right = size;
while (left < right) {
int mid = (left + right) / 2;
if (arr[mid] < target) {
left = mid + 1; // 如果中间元素小于目标值,在右半部分继续查找左侧边界
}
else {
right = mid; // 如果中间元素大于等于目标值,在左半部分继续查找左侧边界
}
}
return left; // 返回左侧边界的下标
}
int main() {
int arr[] = { 1, 2, 2, 2, 4, 5, 6, 7, 8, 9 };
int target = 0;
scanf_s("%d", &target);
int result = leftBoundBinarySearch(arr, target, sizeof(arr) / sizeof(arr[0]));
printf("左侧边界的下标是 %d\n", result);
return 0;
}