【算法】(2)查找

文章目录

  • 二分查找
    • 基础
    • 通用框架
    • 基本二分查找
    • 寻找左侧/右侧边界

二分查找

参考
labuladong : 我写了首诗,让你闭着眼睛也能写对二分搜索

基础

  • 二分查找一般适用于有序数组,这样每一遍循环可以排除掉一半的数字,时间复杂度为O(logn)
  • 特殊情况下,只需要保证每一遍循环可以确定待查找数字位于左区间或右区间,既可以使用二分查找

通用框架

int binarySearch(int[] nums, int target) {
     
    int left = 0, right = ...;

    while(...) {
     
        int mid = left + (right - left) / 2;
        if (nums[mid] == target) {
     
            ...
        } else if (nums[mid] < target) {
     
            left = ...
        } else if (nums[mid] > target) {
     
            right = ...
        }
    }
    return ...;
}
  • 代码中left + (right - left) / 2 就和 (left + right) / 2 的结果相同,但是有效防止了 left 和 right 太大直接相加导致溢出

基本二分查找

int binarySearch(int[] nums, int target) {
     
    int left = 0; 
    int right = nums.length - 1; // 注意

    while(left <= right) {
      //注意
        int mid = left + (right - left) / 2;
        if(nums[mid] == target)
            return mid; 
        else if (nums[mid] < target)
            left = mid + 1; // 注意
        else if (nums[mid] > target)
            right = mid - 1; // 注意
    }
    return -1;
}
  • 使用闭区间作为边界条件,因此
int left = 0; 
int right = nums.length - 1;

索引即代表待查元素区间[left,right]

  • 缩小区间时,
left = mid + 1;
right = mid - 1;

这里直接在下轮查找区间中去掉了mid元素,这样会造成left==right的情况,因此,需要在while条件中考虑这种情况,即while(left <= right)

寻找左侧/右侧边界

  • 如果存在重复目标元素,我们需要查找最左侧的,应该怎样处理?
    例:1,2,2,2,3中查找2,使用上面的算法会返回索引为2的元素,查找最左侧时,我们需要定位到索引为1的元素;查找最右侧时,我们需要定位到索引为3的元素。
  • 实现如下:
public class LeftBoundBinarySearch implements Search {
     
    @Override
    public int search(int[] nums, int target) {
     
        int left = 0 ;
        int right = nums.length - 1;
        while(left <= right){
     
            int mid = left + (right-left)/2 ;
            if(nums[mid] >= target){
     
                right = mid - 1;
            }else{
     
                left = mid + 1;
            }
        }
        if(left == nums.length || nums[left]!=target){
     
            return -1;
        }
        return left;
    }
}
public class RightBoundBinarySearch implements Search {
     
    @Override
    public int search(int[] nums, int target) {
     
        int left = 0 ;
        int right = nums.length - 1;
        while(left <= right){
     
            int mid = left + (right-left)/2 ;
            if(nums[mid] <= target){
     
                left = mid + 1;
            }else {
     
                right = mid - 1;
            }
        }
        if(right == -1 || nums[right]!=target){
     
            return -1;
        }
        return right;
    }
}
  • 解释
  1. nums[mid] == target时,我们没有直接返回,而是继续缩减左右边界,查找左/右边还有没有target元素
  2. 正常情况下,查找最左侧元素结束后,left指向最左侧元素,查找最右侧元素结束后,right指向最右侧元素。
  3. 特殊情况下,需要进行判断:
if (left == nums.length || nums[left] != target)
        return -1;
if(right == -1 || nums[right]!=target){
     
            return -1;
}

while循环结束后,left-right=1是一定成立的, 而left和right指针范围是-1到length,因此left的越界条件是length,right的越界条件是-1

你可能感兴趣的:(算法)