欢迎来到我的:世界
希望作者的文章对你有所帮助,有不足的地方还请指正,大家一起学习交流 !
前言
二分查找算法,也称为折半查找算法,是一种在有序数组中查找特定元素的高效搜索算法。
算法原理:二分查找算法的核心原理是利用数组的有序性,每次将查找范围缩小一半。
它首先取数组中间的元素与目标元素进行比较,如果中间元素恰好是目标元素,则查找成功;
如果目标元素小于中间元素,那么目标元素只可能存在于数组的前半部分,于是可以忽略后半部分,继续在前半部分中进行查找;
如果目标元素大于中间元素,则目标元素只可能存在于数组的后半部分,同样可以忽略前半部分,继续在后半部分中进行查找。
重复上述过程,直到找到目标元素或者确定目标元素不存在于数组中。
内容
详细介绍:二分查找算法,我可以具体分为两种:
1.朴素二分查找算法
,2.边界二分查找算法
接下来我将用第一题来介绍朴素二分查找算法;
用第二,三题介绍边界二分查找算法;
第一题:二分查找
解题思路:这题比较简单,因为他直给了一个升序导数组,所以应该一瞬间就能想到二分查找算法,至于朴素二分查找,简单介绍:就是利用他最关键的升序导条件,可以知道这数组的单调性,利用这点就解决此题;
步骤:
初始化两个指针,left 指向数组起始位置(索引为 0),right 指向数组末尾位置(索引为 nums.size() - 1)。
进入循环,只要 left <= right,就继续查找:
计算中间位置 mid = left + (right - left) / 2,这样可避免 left + right 导致的整数溢出。
将 nums[mid] 与 target 比较:
若相等,直接返回 mid。
若 nums[mid] > target,说明目标值在左半部分,更新 right = mid - 1。
若 nums[mid] < target,说明目标值在右半部分,更新 left = mid + 1。
循环结束还未找到,则返回 -1 表示目标值不存在。
代码实现:
#include <vector>
using namespace std;
class Solution {
public:
int search(vector<int>& nums, int target) {
int left = 0, right = nums.size() - 1;
while (left <= right) {
int mid = left + (right - left) / 2;
if (nums[mid] == target) {
return mid;
} else if (nums[mid] > target) {
right = mid - 1;
} else {
left = mid + 1;
}
}
return -1;
}
};
这里可以看到,这不就是基本的二分查找,为啥叫朴素二分查找,因为他简单啊,这种类型几乎可以利用这一个模板解决所有的类似的题了,接下来我将介绍边界二分查找算法思路:
第二题 山脉数组的峰顶索引
解题思路:对于山脉数组,我们可以利用其先增后减的特性进行二分查找。但是不能在利用朴素的二分查找了,这时候我们的边界二分查找就登场了;
先来了解一下这个算法:
在常规二分查找中,是在有序数组中查找一个确切的目标值。而边界二分查找是在有序数组中查找满足某种条件的边界元素,比如查找第一个大于等于目标值的元素位置,或第一个小于等于目标值的元素位置等。它基于二分查找每次将查找范围减半的思想,通过对中间元素的判断和调整查找区间,逐步逼近目标边界。
初始化指针:设置两个指针,left指向数组的起始位置,即left = 0;right指向数组的末尾位置,即right = arr.size() - 1。
二分查找循环:使用一个while循环,只要left < right,就继续查找。因为当left == right时,就找到了峰值元素的下标。
计算中间位置:在每次循环中,计算中间位置mid = left + (right - left) / 2。这样计算可以避免left + right可能导致的整数溢出问题。
比较与调整指针:比较arr[mid]和arr[mid + 1]的大小:
如果arr[mid] < arr[mid + 1],说明从mid到mid + 1这一段数组是递增的,那么峰值元素必然在mid的右侧。因此,更新left = mid + 1,将查找范围缩小到右半部分。
如果arr[mid] > arr[mid + 1],说明从mid到mid + 1这一段数组是递减的,那么峰值元素要么是mid,要么在mid的左侧。所以,更新right = mid,将查找范围缩小到左半部分。
返回结果:当循环结束时,left和right会指向同一个位置,这个位置就是峰值元素的下标,直接返回left即可。
该题的算法流程图:
代码实现:
class Solution {
public:
int peakIndexInMountainArray(vector<int>& arr) {
int left=0,right=arr.size()-1;
while(left<right)
{
int mid=left+(right-left+1)/2;
if(arr[mid]>=arr[mid-1])left=mid;
else right=mid-1;
}
return left;
}
};
第三题:在排序数组中查找元素的第一个和最后一个位置
这题同样用到边界二分查找算法,利用二分查找的特性,查找到两个边界值即可;
先查找左边值
//找左边界位置
while(left<right)
{
int mid=left+(right-left)/2;
if(nums[mid]>=target)right=mid;
else if(nums[mid]<target)left=mid+1;
}
int begin=0;
//确认左边界
if(nums[right]!=target)return {-1,-1};
else begin=right;
循环条件:while(left < right) 表示只要左指针left小于右指针right,就继续在当前区间内查找。
计算中间位置:int mid = left + (right - left) / 2 计算当前查找区间的中间位置mid,这种写法可以避免left + right可能导致的整数溢出问题。
比较与指针调整:
如果nums[mid] >= target,说明目标值的左边界可能在当前中间位置或者左侧,所以将右指针right更新为mid,缩小查找范围到左半部分。
如果nums[mid] < target,说明目标值在当前中间位置的右侧,将左指针left更新为mid + 1,继续在右半部分查找。
确认左边界:循环结束后,left和right会指向同一个位置。此时判断nums[right]是否等于target,如果不相等,说明数组中不存在目标值,返回{-1, -1};如果相等,则将该位置赋值给begin,作为目标值的左边界。
再查找右边值
//找最右边界
left=0,right=nums.size()-1;
while(left<right)
{
int mid=left+(right-left+1)/2;
if(nums[mid]<=target)left=mid;
else if(nums[mid]>target)right=mid-1;
}
//确认右边界
int end;
if(nums[left]!=target)return {-1,-1};
else end=left;
指针初始化:重新将左指针left设为 0,右指针right设为数组的末尾位置nums.size() - 1,开始查找目标值的右边界。
循环与计算:while(left < right) 为循环条件。这里计算中间位置的表达式int mid = left + (right - left + 1) / 2 加 1 是为了在left和right差值为 1 时,确保mid能取到right的值,避免陷入死循环。
比较与指针调整:
如果nums[mid] <= target,说明目标值的右边界可能在当前中间位置或者右侧,将左指针left更新为mid,继续在右半部分查找。
如果nums[mid] > target,说明目标值在当前中间位置的左侧,将右指针right更新为mid - 1,缩小查找范围到左半部分。
确认右边界:循环结束后,判断nums[left]是否等于target,若不相等,返回{-1, -1};若相等,则将该位置赋值给end,作为目标值的右边界。
代码实现:
class Solution {
public:
vector<int> searchRange(vector<int>& nums, int target) {
int left=0,right=nums.size()-1;
//特殊判断为字符的情况
if(nums.size()==0)return {-1,-1};
//找左边界位置
while(left<right)
{
int mid=left+(right-left)/2;
if(nums[mid]>=target)right=mid;
else if(nums[mid]<target)left=mid+1;
}
int begin=0;
//确认左边界
if(nums[right]!=target)return {-1,-1};
else begin=right;
//找最右边界
left=0,right=nums.size()-1;
while(left<right)
{
int mid=left+(right-left+1)/2;
if(nums[mid]<=target)left=mid;
else if(nums[mid]>target)right=mid-1;
}
//确认右边界
int end;
if(nums[left]!=target)return {-1,-1};
else end=left;
return {begin,end};
}
};
总结
到了最后:感谢支持
------------对过程全力以赴,对结果淡然处之