二分模板(左右查询)

复制代码
public class BinarySearch {
    public static int Binarysearch(int[] a, int target) {
        int i = 0 , j = a.length - 1; //设置指针和初值
        while (i <= j) { //范围内有东西
            int m = (i + j) >>> 1; // i~j;
            if(target < a[m]) { // 目标在左边
                j = m - 1;
            } else if(a[m] < target) { //目标在右边
                i = m + 1;
            } else {
                return m; //找到
            }
        }
        return -1;
    }
}
 /*
   1  i<=j 而不是 i<j 的原因
      i j 它们指向的元素也会参与比较
   2  (i + j) / 2 为什么不这么写
      Java  同一个二进制数
          10111_11111_11111_11111_11111_11110
          不把最高位视为符号位,代表 3221225470
          把最高位视为符号位,代表 - 1073741826
   3  为什么都写成小于号
         代码可读性高,
 */

在 Java 中,二分查找(Binary Search)的 "左查询" 和 "右查询" 通常用于在有序数组中精确查找目标元素的第一个出现位置 (左查询)或最后一个出现位置(右查询)。这两种查询在处理存在重复元素的数组时非常有用。

核心场景

当数组中存在重复元素时,普通二分查找可能返回任意一个匹配元素的位置,而左 / 右查询可以精准定位到重复元素的边界:

  • 左查询:找到目标元素第一次出现的索引(最左侧)。
  • 右查询:找到目标元素最后一次出现的索引(最右侧)。

实现逻辑(基于有序数组)

假设数组是升序排列的,核心思路是通过调整二分查找的边界收缩条件,控制最终定位到左边界或右边界。

1. 左查询(找第一个出现的位置)

目标 :在数组中找到目标值 target 第一次出现的索引,若不存在则返回 -1。

关键逻辑 :当中间元素 nums[mid] == target 时,不立即返回,而是继续向左收缩右边界(right = mid - 1),确保找到更左侧的匹配元素。

复制代码
public static int leftBound(int[] nums, int target) {
    int left = 0;
    int right = nums.length - 1;
    int result = -1; // 初始化为-1(默认不存在)

    while (left <= right) {
        int mid = (right + left)>>>1;
        if (nums[mid] == target) {
            result = mid; // 记录当前位置(可能是左边界)
            right = mid - 1; // 继续向左找,缩小右边界
        } else if (nums[mid] < target) {
            left = mid + 1; // 目标在右侧,缩小左边界
        } else {
            right = mid - 1; // 目标在左侧,缩小右边界
        }
    }
    return result;
}
2. 右查询(找最后一个出现的位置)

目标 :在数组中找到目标值 target 最后一次出现的索引,若不存在则返回 -1。

关键逻辑 :当中间元素 nums[mid] == target 时,不立即返回,而是继续向右收缩左边界(left = mid + 1),确保找到更右侧的匹配元素。

复制代码
public static int rightBound(int[] nums, int target) {
    int left = 0;
    int right = nums.length - 1;
    int result = -1; // 初始化为-1(默认不存在)

    while (left <= right) {
        int mid = (right + left)>>>1;

        if (nums[mid] == target) {
            result = mid; // 记录当前位置(可能是右边界)
            left = mid + 1; // 继续向右找,缩小左边界
        } else if (nums[mid] < target) {
            left = mid + 1; // 目标在右侧,缩小左边界
        } else {
            right = mid - 1; // 目标在左侧,缩小右边界
        }
    }
    return result;
}

实际操作

复制代码
import java.util.Scanner;
public class Main{
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        int n=sc.nextInt();
        int[] nums = new int[n];
        for (int i = 0; i < n; i++) {
            nums[i] = sc.nextInt();
        }
        int target = scanner.nextInt();
        // 3. 调用左查询和右查询方法
        int leftIndex = leftBound(nums, target);
        int rightIndex = rightBound(nums, target);

        // 4. 输出结果
      System.out.println("leftIndex: " + leftIndex + ",rightIndex: " + rightIndex);
    }

    // 左查询:找第一个出现的位置
    private static int leftBound(int[] nums, int target) {
        int left = 0;
        int right = nums.length - 1;
        int result = -1;
        while (left <= right) {
            int mid = (right + left)>>>1; // 避免溢出
            if (nums[mid] == target) {
                result = mid; // 记录当前位置,继续向左找
                right = mid - 1;
            } else if (nums[mid] < target) {
                left = mid + 1; // 目标在右侧
            } else {
                right = mid - 1; // 目标在左侧
            }
        }
        return result;
    }

    // 右查询:找最后一个出现的位置
    private static int rightBound(int[] nums, int target) {
        int left = 0;
        int right = nums.length - 1;
        int result = -1;
        while (left <= right) {
            int mid = (right + left)>>>1;
            if (nums[mid] == target) {
                result = mid; // 记录当前位置,继续向右找
                left = mid + 1;
            } else if (nums[mid] < target) {
                left = mid + 1; // 目标在右侧
            } else {
                right = mid - 1; // 目标在左侧
            }
        }
        return result;
    }
}
相关推荐
小七在进步1 小时前
C++内存管理:new/delete vs malloc/free深度解析
android·java·c++
yuniko-n1 小时前
【JUC】公平锁与非公平锁
java
郑州光合科技余经理1 小时前
本地生活服务系统:预约状态和进度通知接口怎么拆
java·开发语言·前端·javascript·后端·php·生活
靠沿1 小时前
MySQL数据库基础——事务
java·数据库·mysql
蒲公英eric1 小时前
线性回归与正则化:从最小二乘到岭回归、LASSO
人工智能·算法·数学建模·线性回归·岭回归·最小二乘·lasso
碧海银沙音频科技研究院5 小时前
炬芯ATS3089X手表的HFP通话不自动路由到设备BT的问题解决
网络·人工智能·嵌入式硬件·算法
Joy T7 小时前
Agent 确定性上下文设计:从 Prompt 参数注入到能力分层
java·开发语言·微服务·架构·prompt·agent context
m0_5873830010 小时前
广州 24 小时自助健身房解决方案技术实现与实战指南
java·spring·小程序·架构·需求分析