查找算法之二分查找

查找算法之二分查找

简单介绍二分查找

二分查找是一种效率较高的查找方法,要求线性表必须采用顺序存储结构,其时间复杂度为O(log2n)

  • 二分查找的条件:必须是有序数组

  • 二分查找的思想——将目标值和有序数组的中间值进行比较:

​ 当目标值=有序数组中间值时,查找成功;

​ 当目标值<有序数组中间值时,则目标值只可能在左侧,改变右边界;

​ 当目标值>有序数组中间值时,则目标值只可能在右侧,改变左边界;

当左右边界相等时,区间缩成一个点,循环结束,若此时目标值与有序数组中间值仍然不相等,那么目标值不在有序数组中。

例1、在有序数组中查找目标值

题目

给定一个 n 个元素有序的(升序)整型数组 nums 和一个目标值 target ,写一个函数搜索 nums 中的 target,如果目标值存在返回下标,否则返回 -1。

查找算法之二分查找_第1张图片

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/binary-search

解题思路

锁定关键条件——“升序数组”,那么在比较nums[i]与目标值target时想到优先采用二分查找法来寻找目标值。

首先定义left和right分为为用于比较的数组的左边界和右边界。

每一次都先找到数组范围的中点mid,比较nums[mid]和target的大小:

  • 若nums[mid] = target,则mid为要寻找的小标,返回mid
  • 若nums[mid] > target,则target只可能在左侧,此时右边界right改变
  • 若nums[mid] < target,则target只可能在右侧,此时左边界left改变

每次进行二分查找都会将查找范围缩小一半,当left>right时结束查找,即target不在数组中,此时返回-1。

代码

class Solution {
    public int search(int[] nums, int target) {
        /*
        定义一个变量left存储最左边(最小)的值的下标
        同理right存储最右边(最大)的值的小标
         */
        //由于数组下标是从0开始的,所以right的定义需要-1
       int left = 0,right = nums.length - 1;
       while (left <= right){
           /*
           mid为数组最中间的值的下标
           mid的定义可以举例去思考更容易理解
            */
           int mid = (right - left)/2 +left;
           if (nums[mid] == target){
               return mid;
           }else if(nums[mid] > target){
               /*
               由于是升序数组,所以当中间值大于目标值时,
               说明目标值只可能在左侧,所以右边界right发生改变
                */
               right = mid -1;
           }else {
               /*
               同理:
               由于是升序数组,所以当中间值小于目标值时,
               说明目标值只可能在右侧,所以左边界left发生改变
                */
               left = mid +1;
           }
       }
        return -1;
    }
}

例2、找出导致之后所有版本出错的第一个错误版本

题目

你是产品经理,目前正在带领一个团队开发新的产品。不幸的是,你的产品的最新版本没有通过质量检测。由于每个版本都是基于之前的版本开发的,所以错误的版本之后的所有版本都是错的。

假设你有 n 个版本 [1, 2, …, n],你想找出导致之后所有版本出错的第一个错误的版本。

你可以通过调用 bool isBadVersion(version) 接口来判断版本号 version 是否在单元测试中出错。实现一个函数来查找第一个错误的版本。你应该尽量减少对调用 API 的次数。

查找算法之二分查找_第2张图片

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/first-bad-version

解题思路

根据题意,当某个版本为正确版本,则该版本之前所有版本都是正确版本;同理当某个版本为错误版本,则该版本之后所有版本都是错误版本。抓住这一特性,结合题意要求”尽量减少调用API的次数“,所以我们优先想到二分查找法。

确定左右边界之后,根据左右边界计算中间版本,并判断是否为正确版本:

  • 若该版本是正确版本,则第一个错误版本肯定在右侧,所以左边界改变
  • 若该版本是错误版本,则第一个错误要么是该版本要么在左侧,所以右边界改变

代码

public class Solution extends VersionControl {
    public int firstBadVersion(int n) {
        //没有用数组存储,所以开始边界初值为1
        int left = 1,right = n;
        while (left < right){
            int mid = (right - left)/2 +left;
            //调用了力扣的接口isBadVersion
            if (isBadVersion(mid)){
                /*
                如果中间版本是错误版本,则第一个错误要么
                是该版本要么在左侧(具体的还是需要进一步的判断),
                所以右边界改变
                 */
                //注意right没有定义为mid-1,是因为错误版本可能就是中间版本
                right = mid;
            }else {
                /*
                若中间版本是正确版本,那么第一个错误版本肯定在右侧,
                所以左边界改变
                 */
                left = mid +1;
            }
        }
        //此时left=right,区间为一个点,也就是答案
        return right;
    }
}

例3、在一个排序数组中搜索目标值的插入位置

题目

给定一个排序数组和一个目标值,在数组中找到目标值,并返回其索引。如果目标值不存在于数组中,返回它将会被按顺序插入的位置。

请必须使用时间复杂度为 O(log n) 的算法。
查找算法之二分查找_第3张图片

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/search-insert-position

解题思路

同样的先确定左右边界,根据左右边界计算中间下标mid;对nums[mid]和target进行比较:

  • 若相等,则返回下标mid;
  • 若nums[mid]
  • 若nums[mid]>target,则右边界right左移;

假设插入位置为pos,那么nums[pos-1]

代码

class Solution {
    public int searchInsert(int[] nums, int target) {
        int n = nums.length;
        /*
        ans初值设置为数组长度可以省略边界条件的判断,
        以为存在一种情况是target大于数组中的所有数,
        此时需要插入到数组长度的位置
         */
        int left = 0,right = n-1,ans  = n;
        /*
        当你插入进去后,目标值也就成为了nums[pos],
        所以nums[pos]<=target
        while (left <= right){
            int mid =((right - left) >>1) +left;
            if (target <= nums[mid]){
                ans = mid;
                right = mid -1;
            }else {
                left = mid +1;
            }
        }
        return ans;
    }
}

注意:

java中<<、>>、>>>三种移位运算符:

查找算法之二分查找_第4张图片

你可能感兴趣的:(算法入门,算法,leetcode,数据结构,二分查找,java)