二分查找算法

二分查找算法

二分查找是一种在有序数组中查找特定元素的算法。其基本思想是将目标值与数组中间的元素进行比较:

  • 如果目标值等于中间元素,查找成功,返回该元素的索引。
  • 如果目标值小于中间元素,说明目标值在数组的左半部分,更新搜索区间为左半部分。
  • 如果目标值大于中间元素,说明目标值在数组的右半部分,更新搜索区间为右半部分。

这个过程将重复进行,直到找到目标值或搜索区间为空。

前提条件

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

边界条件处理

文章中提到了两种不同的区间定义,这影响了边界条件的处理方式:

  1. 左闭右闭区间 [left, right]

    • while (left <= right) 循环条件,因为当 left 和 right 相等时,区间仍然有效。
    • 如果 nums[middle] > target,则 right = middle - 1,排除中间元素。
  2. 左闭右开区间 [left, right)

    • while (left < right) 循环条件,因为当 left 和 right 相等时,区间无效。
    • 如果 nums[middle] > target,则 right = middle,不包括中间元素。

代码实现

文章提供了两种写法的代码实现,包括详细的注释,解释了每一步操作的原因。

时间与空间复杂度

  • 时间复杂度:O(log n),因为每次比较将搜索空间减半。
  • 空间复杂度:O(1),因为除了输入数组外,只需要常数级别的额外空间。

这段Java代码提供了两种实现二分查找算法的版本,一种是左闭右闭区间的实现,另一种是左闭右开区间的实现。下面是对这两个版本的通俗详细解释:

class Solution {
    public int search(int[] nums, int target) {
        // 避免当 target 小于nums[0] nums[nums.length - 1]时多次循环运算
        if (target < nums[0] || target > nums[nums.length - 1]) {
            return -1;
        }
        int left = 0, right = nums.length - 1;
        while (left <= right) {
            int mid = left + ((right - left) >> 1);
            if (nums[mid] == target) {
                return mid;
            }
            else if (nums[mid] < target) {
                left = mid + 1;
            }
            else { // nums[mid] > target
                right = mid - 1;
            }
        }
        // 未找到目标值
        return -1;
    }
}

### 版本一:左闭右闭区间 [left, right]
在这个版本中,初始时,搜索区间是 `[left, right]`,表示包括数组的第 `left` 项和第 `right` 项。数组的索引从0开始,所以 `nums.length - 1` 是数组最后一个元素的索引。

1. 首先检查目标值 `target` 是否在数组的边界内。如果不在,直接返回-1,表示未找到。
2. 初始化左右指针 `left` 和 `right`,分别指向数组的起始和结束。
3. 在 `while` 循环中,使用位运算符 `>> 1` 计算中间索引 `mid`,这是一种高效的除以2的操作,同时防止可能的整数溢出。
4. 比较 `nums[mid]` 和 `target`:
   - 如果相等,返回 `mid`,表示找到了目标值。
   - 如果 `nums[mid]` 小于 `target`,则将 `left` 更新为 `mid + 1`,排除左边的元素。
   - 如果 `nums[mid]` 大于 `target`,则将 `right` 更新为 `mid - 1`,排除右边的元素。
5. 如果循环结束仍未找到目标值,返回-1。

class Solution {
    public int search(int[] nums, int target) {
        int left = 0, right = nums.length;
        while (left < right) {
            int mid = left + ((right - left) >> 1);
            if (nums[mid] == target) {
                return mid;
            }
            else if (nums[mid] < target) {
                left = mid + 1;
            }
            else { // nums[mid] > target
                right = mid;
            }
        }
        // 未找到目标值
        return -1;
    }
}

### 版本二:左闭右开区间 [left, right)
这个版本与版本一类似,但有一些关键的区别:
- 初始搜索区间是 `[left, right)`,表示不包括数组的第 `right` 项。
- 循环条件使用 `<` 而不是 `<=`,因为 `right` 索引在结束时是不包括在内的(右开区间)。
- 更新 `right` 时,直接设置为 `mid`,因为 `right` 已经是开区间,不需要减1。

### 共同点
- 两种版本都使用了 `while` 循环和位运算来避免整数溢出。
- 两种版本在找到目标值时返回相应的索引,在未找到时返回-1。

### 差异
- 区间的定义不同,导致循环条件和更新边界索引的方式不同。

这些代码示例清晰地展示了二分查找算法的实现,并考虑了整数溢出的问题,通过使用位运算符 `>>` 来安全地计算中间索引。同时,通过预检查目标值是否在数组边界内,避免了无意义的循环运算。
 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值