二分查找(结合蓝桥杯青蛙过河分析)

1、二分查找的思想

二分查找是一种常见的算法,在计算机科学中被广泛使用。其核心思想是将一个有序的数组分成两个部分,然后对其中一个部分进行逐步缩小范围的查找直到找到目标值或者无法继续缩小范围为止。

在二分查找中,需要给出一个有序的数组和一个目标值。首先,将数组分成两个部分,中间点通常是数组的中间值,将中间值与目标值进行比较,如果目标值大于中间值,则在右半部分继续查找;如果目标值小于中间值,则在左半部分继续查找;否则中间值即为目标值。再将找到的部分进行递归二分查找,直到找到目标值或找不到为止。

代码实现

下面是一段基于C++的二分查找代码实现:

int binary_search(int arr[], int n, int target) {
    int left = 0, right = n - 1;
    while (left <= right) {
        int mid = (left + right) >> 1;
        if (arr[mid] == target) {
            return mid;
        } else if (arr[mid] < target) {
            left = mid + 1;
        } else {
            right = mid - 1;
        }
    }
    return -1;
}
// 二分查找的原理是将有序数组分成两半,取中间值与目标值进行比较,如果相等则返回中间值的下标,如果中间值小于目标值,则在右半部分继续查找,否则在左半部分继续查找,直到找到目标值或者左右指针相遇仍未找到目标值,返回-1。

在这个实现中,我们首先用左侧索引让 left 指向数组的起始位置,用右侧索引让 right 指向数组的末尾位置。在每一轮查找中,我们用中间索引 mid 指向数组的中间元素,并将目标值与该元素进行比较。如果目标值等于 mid 所指向的元素,则返回 mid;如果目标值大于 mid 所指向元素,则通过移动 left 来缩小查找范围;如果目标值小于 mid 所指向元素,则通过移动 right 缩小查找范围;如果 left 指向的位置后面已经没有元素了,则说明查找失败,返回 -1。

二分查找的时间复杂度是 O(log n),其中 n 是数组的长度。其算法的时间复杂度比顺序查找更快,并且对于较大的有序数组,该算法效果尤为显著。

2、例题——青蛙过河

题目描述

题目链接:青蛙过河 - 蓝桥云课 (lanqiao.cn)

小青蛙住在一条河边,它想到河对岸的学校去学习。小青蛙打算经过河里的石头跳到对岸。
河里的石头排成了一条直线,小青蛙每次跳跃必须落在一块石头或者岸上。不过,每块石头有一个高度,每次小青蛙从一块石头起跳,这块石头的高度就会下降 1,当石头的高度下降到 0 时小青蛙不能再跳到这块石头上(某次跳跃后使石头高度下降到 0 是允许的)。
小青蛙一共需要去学校上 x 天课,所以它需要往返 2x 次。当小青蛙具有一个跳跃能力 y 时,它能跳不超过 y 的距离。
请问小青蛙的跳跃能力至少是多少才能用这些石头上完 x 次课。

思路

首先,我们可以用贪心的思想想到:青蛙往返累计2 x 次相当于单向走2 x 次。

跳跃能力这一要素是我们要求解的对象,跳跃能力越大,越能保证可以通过2 x 次。因此我们可以使用二分的思想,找到一个最小的满足条件的跳跃能力。

具体该怎么想?我们看下面的分析:

假设小青蛙的跳跃能力为mid,那么从起点开始,只要能够跳到一个高度为mid的石头上,就跳到该石头,再从该石头开始跳跃,直到跳到对岸。如果不能跳到高度为mid的石头上,就说明该跳跃能力太小,需要减小mid的值。如果能跳到对岸,继续增大mid的值。通过二分查找最终找到符合条件的最小跳跃能力。

那么我们要做的就是求这个mid了,再结合我们上面分析的贪心的思想,相当于有2x只青蛙一块往前跳,第一次根据这些青蛙会跳到一个区间内,这个区间就是[1,mid],因为mid是他们的跳跃能力,第一次跳最远不会跳出第mid个石头,而我们要求这个mid就要知道这个区间的特征。

扫描二维码关注公众号,回复: 14649452 查看本文章

这个区间有啥特征?

因为可以理解成2x只青蛙一块跳,青蛙要能跳出去的第一步必须要有2x次以上,即[1,mid]的石头的总高度>=2x(为什么是大于等于,而不是等于,是因为青蛙可能不是在下一次跳的时候一定跳出前一个的区间),那么在以后再跳的时候,对于任意石头i,区间[i,i+mid)中的石头可被踩的总数>=2x

因为涉及到区间的总和,所以我们还需要维护一下他的前缀和sum[]

代码

#include <iostream>
using namespace std;
int n,x;
bool judge(int length,int *sum){
  for(int i=1;i<n-length+1;i++){
    if(sum[i+length-1]-sum[i-1]<2*x)return false;
  }
  return true;
}
int main()
{
  // 请在此输入您的代码
  cin>>n>>x;
  int h[n],sum[n];
  sum[0]=0;
  for(int i=1;i<n;i++){
    cin>>h[i];
    sum[i]=h[i]+sum[i-1];
  }
  int l=1,r=n;
  while(l!=r){
    if(judge((l+r)/2,sum))r=(l+r)/2;
    else l=(l+r)/2+1;
  }
  cout<<l;
  return 0;
}

猜你喜欢

转载自blog.csdn.net/m0_52124992/article/details/129976929