704 二分查找

184 阅读2分钟

前言

使用 二分法 的前提:

  • 数组必须是有序的
  • 数组中元素不能重复

为什么不能重复?

如果数组中元素有重复的,那么查找出来的元素就会有多个。

二分法怎么用?

每次都取区间的中间值,来判断所查询的元素在哪一个区间中,每次进行二分的时候,都能舍去另一半的区间不用查找。

比如:我想要查找数组中有没有88,

middle = (left + right ) / 2

middle = (0 + 6) / 2 = 3

接着拿着 nums[middle] 和 target(查找的元素)比较,

如果 nums[middle] < target 说明在右区域,移动 left

如果 nums[middle] > target 说明在左区域,移动 right

image.png

思路

  1. 定义区间(很重要):是左闭右闭,还是左闭右开

你定义的区间决定了你的很多边界条件怎么写,例如到底是 while(left < right) 还是 while(left <= right),到底是right = middle呢,还是要right = middle - 1

如果是左闭右闭:

  • while (left <= right) 要使用 <= ,因为left == right是有意义的,所以使用 <=
  • if (nums[middle] > target) right 要赋值为 middle - 1,因为当前这个nums[middle]一定不是target,那么接下来要查找的左区间结束下标位置就是 middle - 1
class Solution {
    // 区间的定义:左闭右闭[]
    public int search(int[] nums, int target) {
       int left = 0;
       // 右边封闭,需要包含元素
       int right = nums.length - 1;
       while (left <= right) {
          int middle = (left + right) / 2;
          if (nums[middle] == target) {
             return middle;
          } else if (nums[middle] < target) {
             left = middle + 1;
          }else {
             right = middle - 1;
          }
       }
       // 没有找到
       return -1;
    }
}

如果是左闭右开:

  • while (left < right),这里使用 < ,因为left == right在区间[left, right)是没有意义的
  • if (nums[middle] > target) right 更新为 middle,因为当前nums[middle]不等于target,去左区间继续寻找,而寻找区间是左闭右开区间,所以right更新为middle,即:下一个查询区间不会去比较nums[middle]
class Solution {
    // 区间的定义:左闭右开[)
    public int search(int[] nums, int target) {
       int left = 0;
       int right = nums.length;
       while (left < right) {
          int middle = (left + right) / 2;
          // target 在右区域
          if (nums[middle] < target) {
             left = middle + 1;
          }
          // 在 左区域
          else if (nums[middle] > target) {
             right = middle;
          }else {
             return middle;
          }
       }
       // 循环结束,没有找到
       return -1;
    }
}