二分查找。

1. 数组

数组是一种具有连续储存空间的数据结构,具有以下特点:

  1. 起始索引为0
  2. 存储空间的连续性
  3. 添加、删除通过移动数组内元素实现,效率较低
  4. 查询效率较高

2. 二分查找

基本思想:对一升序的int数组,通过不断地修改left、right索引查询中间元素middle是否与目标元素target相同,若 t a r g e t < m i d d l e target < middle target<middle,则 r i g h t = m i d d l e − 1 right = middle - 1 right=middle1;若 t a r g e t > m i d d l e target > middle target>middle,则 l e f t = m i d d l e + 1 left = middle + 1 left=middle+1;若 t a r g e t = m i d d l e target = middle target=middle,则算法返回该值索引。

public class Solution {
    public int binarySearch(int[] arr, int target) {
        int left = 0;
        int right = arr.length - 1; // 搜索范围[left, right]
        if (target < arr[0] || target > arr[right]) return -1;
         // 搜索失败时[right + 1, right] 即left > right
        while (left <= right) {
            int middle = left + ((right - left) >> 1);
            if (arr[middle] == target) {
                return middle;
            } else if (arr[middle] > target) {
                right = middle - 1;
            } else {
                left = middle + 1;
            }
        }
        return -1;
    }
    
}

2.1 搜索插入位置

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

请必须使用时间复杂度为 O(log n) 的算法。
链接:https://leetcode.cn/problems/search-insert-position

思路

给定数组已排好序,且时间复杂度为 O ( l o g n ) \text O(logn) O(logn),考虑使用二分查找。

二分查找搜索情况:

  1. t a r g e t < a r r [ 0 ] target < arr[0] target<arr[0]时, l e f t = 0 left=0 left=0 r i g h t = − 1 right = -1 right=1
  2. t a r g e t > a r r [ r i g h t ] target > arr[right] target>arr[right]时, l e f t = r i g h t + 1 left = right + 1 left=right+1 r i g h t = a r r . l e n g t h − 1 right = arr.length - 1 right=arr.length1
  3. a r r [ 0 ] < = t a r g e t < = a r r [ r i g h t ] arr[0] <= target <= arr[right] arr[0]<=target<=arr[right] t a r g e t target target不存在数组时,有 a r r [ r i g h t ] < t a r g e t < a r r [ l e f t ] arr[right]< target < arr[left] arr[right]<target<arr[left]
  4. 当查询成功时,有 i n d e x   i n   { 1 , . . . , n } index\ in \ \{1,...,n\} index in {1,...,n}

综上所述:对于查找成功地元素则直接返回其索引。而对于查找失败地元素,则返回其 l e f t left left值作为插入位置。

public class Solution {
    public int searchInsert(int[] arr, int target) {
        int left = 0;
        int right = arr.length - 1;
        while (left <= right) {
            int middle = left + ((right - left) >> 1);
            if (arr[middle] == target) {
                return middle;
            } else if (arr[middle] > target) {
                right = middle - 1;
            } else {
                left = middle + 1;
            }
        }
        return left;
    }
}

2.2 在排序数组中查找元素的第一个和最后一个位置

思路来自:https://blog.csdn.net/weixin_43373833/article/details/113763879?spm=1001.2014.3001.5506

给你一个按照非递减顺序排列的整数数组 nums,和一个目标值 target。请你找出给定目标值在数组中的开始位置和结束位置。

如果数组中不存在目标值 target,返回 [-1, -1]。

你必须设计并实现时间复杂度为 O(log n) 的算法解决此问题。
链接:https://leetcode.cn/problems/find-first-and-last-position-of-element-in-sorted-array

示例 1:

输入:nums = [5,7,7,8,8,10], target = 8
输出:[3,4]
示例 2:

输入:nums = [5,7,7,8,8,10], target = 6
输出:[-1,-1]
示例 3:

输入:nums = [], target = 0
输出:[-1,-1]

思路

需要注意,非递减数组,因此,数组中存在重复元素。且需要设计一个 O ( l o g n ) O(log n) O(logn)的方法,因此考虑二分查找。

题目意思是寻找某一元素的左边界和右边界。因此,对于常规的二分查找算法,需要作一定的修改。

当算法查找 t a r g e t target target成功时:

  1. 若寻找左边界,则需要把搜索范围缩小,即 r i g h t = m i d d l e − 1 right = middle - 1 right=middle1。当到达左边界后,有 r i g h t right right在目标值的左边,而退出循环的条件为 l e f t = r i g h t + 1 left = right + 1 left=right+1,所以 a r r [ l e f t ] = t a r g e t arr[left] = target arr[left]=target
  2. 若寻找右边界,则 l e f t = m i d d l e + 1 left = middle + 1 left=middle+1,同理 a r r [ r i g h t ] = t a r g e t arr[right] = target arr[right]=target

每查找成功一次,则记录其索引值。

当算法查找失败时,考虑超出边界、和不超出边界两种情况:

  1. 查询左边界时
    • t a r g e t target target大于数组中的所有元素时,有 r i g h t = a r r . l e n g t h − 1 right = arr.length - 1 right=arr.length1 l e f t = r i g h t + 1 left = right + 1 left=right+1,所以, l e f t > = a r r . l e n g t h left >= arr.length left>=arr.length
    • t a r g e t target target没有超出边界,但不存在时,有 a r r [ l e f t ] ! = t a r g e t arr[left] != target arr[left]!=target
  2. 查询右边界时
    • t a r g e t target target小于数组中的所有元素时,有 r i g h t = − 1 right = -1 right=1 l e f t = 0 left = 0 left=0,所以, r i g h t < 0 right < 0 right<0
    • t a r g e t target target不超出边界,但不存在时,有 a r r [ r i g h t ] ! = t a r g e t arr[right] != target arr[right]!=target
// 查询左边界
public int searchLeftBoard(int[] arr, int target) {
    int left = 0;
    int right = arr.length - 1;
    while (left <= right) {
        int middle = left + ((right - left) >> 1);
        if (arr[middle] == target) {
            //收缩查询范围
            right = middle - 1;
        } else if (arr[middle] > target) {
            right = middle - 1;
        } else {
            left = middle + 1;
        }
    }
    // 先判断是否超出边界,在判断arr[left]与target的值
    if (left >= arr.length || arr[left] != target) {
        return -1;
    }
    return left;
}

// 查询右边界
public int searchRightBoard(int[] arr, int target) {
    int left = 0;
    int right = arr.length - 1;
    while (left <= right) {
        int middle = left + (right - left) >> 1);
        if (arr[middle] == target) {
            //收缩查询范围
            left = middle + 1;
        } else if (arr[middle] > target) {
            right = middle - 1;
        } else {
            left = middle + 1;
        }
    }
    // 先判断是否超出边界,在判断arr[right]与target的值
    if (right < 0 || arr[right] != target) {
        return -1;
    }
    return right;
}

3. 总结

参考的那篇二分查找的文章非常好,思想上非常nice!

总结的规律

前提条件:设target为查询的目标对象,arr为整数数组(不含重复元素)。(注意这里一定是要整数数组,浮点数需要特别小心)且不存在重复元素时,有:

  • target < a r r [ 0 ] \text{target} < arr[0] target<arr[0]时,有 left = 0 , right = − 1 \text{left} =0,\text{right} = -1 left=0,right=1
  • target > a r r [ arr.length - 1 ] \text{target} > arr[\text{arr.length - 1}] target>arr[arr.length - 1]时,有 right = arr.length - 1, left = arr.length \text{right = arr.length - 1, left = arr.length} right = arr.length - 1, left = arr.length
  • arr[0] < target < arr[arr.length - 1] and target not in arr为true \text{arr[0] < target < arr[arr.length - 1] and target not in arr为true} arr[0] < target < arr[arr.length - 1] and target not in arrtrue,有 arr[right] < target < arr[left] \text{arr[right] < target < arr[left]} arr[right] < target < arr[left]

若数组(含重复元素)存在重复元素且非递减,需要某个元素的第一个位置或最后一个位置。思路时不断缩小搜索区域。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值