ICode9

精准搜索请尝试: 精确搜索
首页 > 编程语言> 文章详细

五大算法之二分搜索

2022-07-28 00:33:21  阅读:172  来源: 互联网

标签:二分 right 边界 nums int 算法 搜索 left


概述

  二分搜索是常见的搜索算法,能够将有序数组搜索的线性复杂度降低到对数级别。搜索过程每次取搜索区间内的中间元素,如果等于目标元素则直接返回结果;如果大于或小于目标元素,则将搜索区间缩短到对应的一半元素范围,继续搜索,直至搜索区间为空。当然二分搜索不限于找目标值,寻找左侧边界、寻找右侧边界也是常见的搜索场景。

时间复杂度:O(logN),搜索区间逐次减半。

空间复杂度:O(1),只占用常数量级空间。

 

核心思想

  二分搜索思路比较简单,正是因为简单,大部分人会忽略细节的理解,比如,是left = mid + 1 还是 right = mid - 1,是 while(left < right) 还是 while(left <= right) ,这些都是经常让人困惑的点。只有理解二分搜索的核心思想,才能在一些细节上不含糊。二分搜索核心要素有以下几点:

  • 搜索区间

搜索区间指的是每次搜索时的元素区间段,是[left, right] 还是 [left, right),这个是在搜索之初就需要确定好的。前闭后闭则循环条件为 left <= right;前闭后开则循环条件为 left < right。

  • 目标边界

目标边界指的是搜索目标在有序数组中的边界条件,通常我们会考虑搜索目标是左边界还是右边界,比如,升序重复数组中查找目标值第一个位置、最后一个位置。

右边界也可以变成左边界,比如,升序重复数组中查找目标值最后一个位置,可以变成大于目标值的第一个位置,结果再减一。

更重要的是,根据边界选择判断条件,无论是左边界还是右边界,都是将搜索区间分成两段,如何区分是二分搜索的关键。


算法框架

  既然右边界搜索可以转化为左边界搜索,单一元素查找也可以转为左边界搜索,那接下来就左边界搜索给出算法的通用框架,基本能够涵盖大部分二分搜索场景。

int findLeftBorder(vector<int>& nums) {
  int left = 0, right = nums.size();
  while (left < right) {
    int mid = left + (right - left) / 2;
    if (满足右分区条件) {
      right = mid;
    } else {
      left = mid + 1;
    }
  }
  return left < nums.size() ? left : -1;
}

例题分析

例题1(LeetCode - 34)

  如题,典型的求目标值左右边界,我们的思路是都转成左边界问题。那么搜索区间我们设置为左闭右开(可以按习惯来,框架给的是左闭右开),则循环条件为 left < right。边界判断条件对应左右边界有所不同,左边界求解划分的右区间判断条件为 nums[i] >= target;右边界求解划分的右区间判断条件为 nums[i] >= target + 1,得到右分区左边界再减1得到最终目标右边界。

将相关逻辑填入框架,代码如下:

class Solution {
public:
    vector<int> searchRange(vector<int>& nums, int target) {
        vector<int> res = {-1, -1};
        int left = findLeftBorder(nums, target);
        int right = findLeftBorder(nums, target + 1) - 1;
        if (left < nums.size() && nums[left] == target) {
            res[0] = left;
            res[1] = right;
        }
        return res;
    }
    int findLeftBorder(vector<int>& nums, int target) {
        int left = 0, right = nums.size();
        while (left < right) {
            int mid = left + (right - left) / 2;
            if (nums[mid] >= target) {
                right = mid;
            } else {
                left = mid + 1;
            }
        }
        return left;
    }
};

例题2(LeetCode - 153)

  如题,同样我们还是转成左边界问题,那么以目标值为界,我们需要将数组分为左右两段,判断条件如何选择呢?如下图所示,我们可以发现右分区所有元素都小于等于最右的元素,当然图中右分区所有元素也都小于最左元素。那是否可以将nums[i] < nums[0] 作为有分区条件呢?不要忽略了一种情况,n次旋转后数组排序不变,那么此时右分区为整个数组,nums[i] < nums[0] 条件不成立,而nums[i] < nums[n - 1]恒成立。

将相关逻辑填入框架,代码如下:

class Solution {
public:
    int findMin(vector<int>& nums) {
        int left = 0, right = nums.size();
        int base = nums[nums.size() - 1];
        while (left < right) {
            int mid = left + (right - left) / 2;
            if (nums[mid] <= base) {
                right = mid;
            } else {
                left = mid + 1;
            }
        }
        return nums[left];
    }
};

 


 

标签:二分,right,边界,nums,int,算法,搜索,left
来源: https://www.cnblogs.com/BobPong/p/16527055.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有