二分查找总结


二分查找要求:
线性表是有序表,即表中结点按关键字有序,并且要用向量作为表的存储结构。

适用情况
二分查找只适用顺序存储结构。为保持表的有序性,在顺序结构里插入和删除都必须移动大量的结点。因此,二分查找特别适用于那种一经建立就很少改动、而又经常需要查找的线性表。

对那些查找少而又经常需要改动的线性表,可采用链表作存储结构,进行顺序查找。链表上无法实现二分查找。

怎么写二分查找

求下界问题:
1.求大于等于target的第一个数;对应标准库中的lower_bound()函数。
2.求大于target的第一个数;对应标准库中的upper_bound()函数。

求上界问题: 等于对应上界问题的解-1
1.求小于target的最后一个数, 等于标准库中的lower_bound()函数的返回值减1。
2.求小于等于target的最后一个数, 等于标准库中的upper_bound()函数的返回值减1。

先看看参考:https://www.zhihu.com/question/36132386

关键点

1.根据你选择的区间是【】,还是【),
2.注意区分:循环条件,中值写法,是否加1,减1,循环结束后的状态
3.可以直接举几个实例看看,如2个元素,3个元素

1.左闭右闭区间【】
对应while(left<=right),
左中值:mid=left+(right-left)/2
循环结束时left>right,1,减1

2.左闭右开【)
对应while(left<right),
右中值:mid=left+(right-left)/2
循环结束时left==right,1,不用减1

参照标准库的lower_bound(),upper_bound()写法,倾向于选择【)来解题

头文件

<algorithm>

lower_bound()功能:返回大于等于val的迭代器
http://www.cplusplus.com/reference/algorithm/lower_bound/

  • 默认版本
template <class ForwardIterator, class T>
 ForwardIterator lower_bound (ForwardIterator first, ForwardIterator last,const T& val);
  • 自定义比较函数版, 返回的是, comp(*迭代器, val)为true时,向右压缩区间;comp(*迭代器, val)为false时,向左压缩区间
template <class ForwardIterator, class T, class Compare>
ForwardIterator lower_bound (ForwardIterator first, ForwardIterator last,const T& val, Compare comp);

看下标准库的实现

template <class ForwardIterator, class T>
  ForwardIterator lower_bound (ForwardIterator first, ForwardIterator last, const T& val)
{
  ForwardIterator it;
  iterator_traits<ForwardIterator>::difference_type count, step;
  count = distance(first,last);
  while (count>0)
  {
    it = first; step=count/2; advance (it,step);
    if (*it<val) {                 // or: if (comp(*it,val)), for version (2)
      first=++it;
      count-=step+1;
    }
    else count=step;
  }
  return first;
}

upper_bound()功能:返回大于val的迭代器
http://www.cplusplus.com/reference/algorithm/upper_bound/

  • 默认版本
template <class ForwardIterator, class T>
  ForwardIterator upper_bound (ForwardIterator first, ForwardIterator last,
                               const T& val);
  • 自定义比较函数版, 返回的是, comp(*迭代器, val)为true时,向右压缩区间;comp(*迭代器, val)为false时,向左压缩区间
template <class ForwardIterator, class T, class Compare>
  ForwardIterator upper_bound (ForwardIterator first, ForwardIterator last,
                               const T& val, Compare comp);

看下标准库的实现

template <class ForwardIterator, class T>
  ForwardIterator upper_bound (ForwardIterator first, ForwardIterator last, const T& val)
{
  ForwardIterator it;
  iterator_traits<ForwardIterator>::difference_type count, step;
  count = std::distance(first,last);
  while (count>0)
  {
    it = first; step=count/2; std::advance (it,step);
    if (!(val<*it))                 // or: if (!comp(val,*it)), for version (2)
      { first=++it; count-=step+1;  }
    else count=step;
  }
  return first;
}

使用示例

// lower_bound/upper_bound example
#include <iostream>     // std::cout
#include <algorithm>    // std::lower_bound, std::upper_bound, std::sort
#include <vector>       // std::vector

int main () {
  int myints[] = {10,20,30,30,20,10,10,20};
  std::vector<int> v(myints,myints+8);           // 10 20 30 30 20 10 10 20

  std::sort (v.begin(), v.end());                // 10 10 10 20 20 20 30 30

  std::vector<int>::iterator low,up;
  low=std::lower_bound (v.begin(), v.end(), 20); //          ^
  up= std::upper_bound (v.begin(), v.end(), 20); //                   ^

  std::cout << "lower_bound at position " << (low- v.begin()) << '\n';
  std::cout << "upper_bound at position " << (up - v.begin()) << '\n';

  return 0;
}

动手写二分查找

  • 左闭右开区间,实现二分查找
//二分查找
int binarySearch(vector<int> nums,int target){
    int left=0,right=nums.size();//左闭右开
    int mid;//右中值
    //循环条件无等号
    while(left<right){
        mid=left+(right-left)/2;//右中值
        if(nums[mid]==target)
            return mid;//找到
        else if(nums[mid]<target)
            left=mid+1;
        else
            right=mid;//区间左闭右开,右边的取不到
    }

    return -1;//没有找到
}
  • 左闭右开区间,实现myLower_bound()
//查找大于等于target的第一个数
int myLower_bound(vector<int> nums,int target){
    int left=0,right=nums.size();//左闭右开
    int mid;
    //循环条件无等号
    while(left<right){
        mid=left+(right-left)/2;//右中值
        if(nums[mid]<target)
            left=mid+1;
        else
            right=mid;//区间左闭右开,右边的取不到
    }
    return left;
}
  • 左闭右开区间,实现myUpper_bound()
//查找大于target的第一个数,
int myUpper_bound(vector<int> nums,int target){
    int left=0,right=nums.size();
    int mid;
    while(left<right){
        mid=left+(right-left)/2;
        if(nums[mid]<=target)
            left=mid+1;
        else
            right=mid;
    }
    return left;
}

完整测试

#include<iostream>
#include<algorithm>
#include<vector>
#include<time.h>
using namespace std;
//二分查找
int binarySearch(vector<int> nums,int target){
    int left=0,right=nums.size();//左闭右开
    int mid;//右中值
    while(left<right){
        mid=left+(right-left)/2;//右中值
        if(nums[mid]==target)
            return mid;//找到
        else if(nums[mid]<target)
            left=mid+1;
        else
            right=mid;//区间左闭右开,右边的取不到
    }

    return -1;//没有找到
}

//查找大于等于target的第一个数,举例【0,1,2】,target=0.1;
int myLower_bound(vector<int> nums,int target){
    int left=0,right=nums.size();//左闭右开
    int mid;
    while(left<right){
        mid=left+(right-left)/2;//右中值
        if(nums[mid]<target)
            left=mid+1;
        else
            right=mid;//
    }
    return left;
}

//查找大于target的第一个数,
int myUpper_bound(vector<int> nums,int target){
    int left=0,right=nums.size();
    int mid;
    while(left<right){
        mid=left+(right-left)/2;
        if(nums[mid]<=target)
            left=mid+1;
        else
            right=mid;
    }
    return left;
}


//测试
int main(){
    vector<int> nums={1,2,2,2,2,3,3,4,5,5,6,7,7,8};
    //随机选择一个数作为target
    srand((unsigned int)time(NULL));
    int index=rand()%nums.size();//0~size()-1
    int target=nums[index];
    //二分查找,只找到一个就返回,不一定是边界值
    cout<<"binarySearch("<<target<<")= "<<binarySearch(nums,target)<<endl;
    //找到大于等于target的第一个值的位置
    cout<<"myLower_bound("<<target<<")= "<<myLower_bound(nums,target)<<endl;
    //找到大于target的第一个值位置
    cout<<"myUpper_bound("<<target<<")= "<<myUpper_bound(nums,target)<<endl;

    //测试下lower_bound()和upper_bound()和equal_range()
    cout<<"equal_range("<<target<<")=["<<(lower_bound(nums.begin(),nums.end(),target)-nums.begin())
        <<","<<(upper_bound(nums.begin(),nums.end(),target)-nums.begin())<<")"<<endl;
    cout<<"equal_range("<<target<<")=["<<equal_range(nums.begin(),nums.end(),target).first-nums.begin()
        <<","<<equal_range(nums.begin(),nums.end(),target).second-nums.begin()<<")"<<endl;
    
    system("pause");
    return 0;
}

在这里插入图片描述

相关例题

剑指offer 面试题11:

旋转数组的最小数字

输入一个递增排序的数组的一个旋转,一个数组的旋转指把数组前面的若干位搬到后面
例如【3,4,5,1,2】是【1,2,3,4,5】的一个旋转

输出数组中的最小数字,这里就是1

思路
方法1:扫描一遍,同时判断当前位的数字大于当前位后一位时,返回当前位后一位的值,时间复杂度O(N)

#include<stdexcept>
int minInOrder(int* nums,int left,int right){
    if(nums==nullptr||left<1||right<1||right<left)
        throw logic_error("Invalid input");
    if(nums[left]<nums[right])//原数组已经有序
        return nums[left];
    for(int i=left;i<right;i++){
        if(nums[i]>nums[i+1])
            return nums[i+1];
    }
    //到这里表明[left,right]中元素全相等
    return nums[left];
}

方法2:
这里可以利用二分查找的思想,left,right,mid=left+(right-left)/2; 这里采用左闭右闭区间。

  • 若num[mid]<=num[right],表明最小数字位于mid左边,即最小数字在区间【left,mid】
  • 若num[mid]>=num[left],表明最小数字位于mid右边,即最小数字在区间【mid+1,right】

还有存在相等元素时的特殊情况【1,0,1,1,1】,【1,1,1,0,1】,此时无法知道最小数字位于mid左边还是右边,只能用方法1顺序扫描。

从这题看出来写好二分查找不容易,区间,有相等元素时的特殊情况【1,0,1,1,1】,【1,1,1,0,1】。

int getMin(int* nums,int length){
    if(nums==nullptr||length<1)
        throw logic_error("Invalid input");
    //左闭右闭区间
    int left=0,right=length-1;
    int mid;
    //注意【1,0,1,1,1】这种,所以不能取等号
    if(nums[left]<nums[right])//原数组已经有序,
        return nums[left];
    //写二分查找时注意测试只有2个元素,只有3个元素的情况
    //这里有一种特殊情况:【1,0,1,1,1】,【1,1,1,0,1】即数组里面出现了重复的元素
    //表现为nums[left]==nums[right]==nums[mid],此时,我们无法判断最小元素在左边还是右边
    //出现这种情况时我们必须在[left,right]内部顺序查找,
    while(left<right){
        mid=left+(right-left)/2;
        //nums[left]==nums[right]==nums[mid],此时,我们无法判断最小元素在左边还是右边
        //出现这种情况时我们必须在[left,right]内部顺序查找,
        if(nums[left]==nums[mid]&&nums[right]==nums[mid])
            return minInOrder(nums,left,right);

        if(nums[mid]>=nums[left])
            left=mid+1;//注意这里只有两个元素时避免死循环,必须加1,
        else if(nums[mid]<=nums[right])
            right=mid;
    }
    return nums[left];
}

爱吃香蕉的珂珂

leetcode 875. 爱吃香蕉的珂珂
https://leetcode-cn.com/problems/koko-eating-bananas/

珂珂喜欢吃香蕉。这里有 N 堆香蕉,第 i 堆中有 piles[i] 根香蕉。警卫已经离开了,将在 H 小时后回来。

珂珂可以决定她吃香蕉的速度 K (单位:根/小时)。每个小时,她将会选择一堆香蕉,从中吃掉 K 根。如果这堆香蕉少于 K 根,她将吃掉这堆的所有香蕉,然后这一小时内不会再吃更多的香蕉。

珂珂喜欢慢慢吃,但仍然想在警卫回来前吃掉所有的香蕉。

返回她可以在 H 小时内吃掉所有香蕉的最小速度 K(K 为整数)。

示例:piles[3,6,7,11],H=8;
输出:4

思路
1)先找出K的范围,[1,max(piles)]
2)K从1到max(piles)递增过程中一旦遇到H小时能吃完香蕉,就返回此时的k
3.延申到lower_bound()函数,这里就是返回区间中满足条件的首个位置
4.这里的将二分查找广义化了,原来的判断大小,压缩区间,这里变成了函数返回true/false来压缩区间

class Solution {
public:
    //判断以速度K是否在H小时内能吃完
    bool canFinish(vector<int>&piles,int K,int H){
        int hours=0;
        for(int i=0;i<piles.size();i++){
            if(piles[i]<=K)    
                hours++;
            else
            {
                hours+=(piles[i]/K +1);//piles[i]/K +1小时内吃完
            } 
        }
        return hours<=H;
    }

    int minEatingSpeed(vector<int>& piles, int H) {
        //piles的最大值
        int maxPiles=INT_MIN;
        for(int i=0;i<piles.size();i++)
        {
            if(piles[i]>maxPiles)
                maxPiles=piles[i];
        }
        int left=1,right=maxPiles+1;//区间左闭右开

        while(left<right){
            int mid=left+(right-left)/2;
            if(canFinish(piles,mid,H))
                right=mid;
            else
            {
                left=mid+1;
            }
        }
        return left;
    }
};
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
在Matlab中,可以使用二分查找法来查找数组中的元素。以下是使用递归实现的二分查找算法的示例代码: ```matlab function BinarySearch_2(array, low, high, value) n = length(array); if low > high fprintf('Sorry, the number does not exist !!! \n\n') else mid = floor((low + high) / 2); if array(mid) == value fprintf('The number is found at position %d. \n\n', mid); elseif array(mid) < value BinarySearch_2(array, mid + 1, high, value); else BinarySearch_2(array, low, mid - 1, value); end end end % 测试数组 BinarySearch_2([1 3 4 6 8 10 13 21 23 46 54 56], 1, 12, 21); ``` 以上代码实现了一个二分查找函数`BinarySearch_2`,接受一个有序数组、数组的下界、上界和要查找的值作为输入。函数会在数组中查找指定的值,并返回它的位置。如果值不存在于数组中,会输出提示信息。 请注意,这只是一个示例代码,你可以根据自己的需求进行修改和扩展。 另外,除了二分查找,Matlab还提供了其他查找方法,比如线性查找、插值查找等。你可以根据具体情况选择合适的查找算法。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* [二分查找法及其四种变形(MATLAB)](https://blog.csdn.net/HuiningM/article/details/100173538)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] - *2* *3* [Matlab中顺序查找、二分搜索及一二三次样条的一些总结](https://blog.csdn.net/m0_62961080/article/details/121879268)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值