二分查找进阶:搜索二维矩阵 & 查找元素首尾位置 深度解析

目录

[一、搜索二维矩阵(LeetCode 74・中等)](#一、搜索二维矩阵(LeetCode 74・中等))

题目描述

解题思路

[Java 代码实现(标准二分版)](#Java 代码实现(标准二分版))

复杂度分析

核心知识点总结

[二、在排序数组中查找元素的第一个和最后一个位置(LeetCode 34・中等)](#二、在排序数组中查找元素的第一个和最后一个位置(LeetCode 34・中等))

题目描述

解题思路

[Java 代码实现(两次二分版)](#Java 代码实现(两次二分版))

复杂度分析

核心知识点总结


一、搜索二维矩阵(LeetCode 74・中等)

题目描述

编写一个高效的算法来判断 m x n 矩阵中,是否存在一个目标值。该矩阵具有如下特性:

  • 每行中的整数从左到右按升序排列。
  • 每行的第一个整数大于前一行的最后一个整数。

示例:

plaintext

复制代码
输入:matrix = [[1,3,5,7],[10,11,16,20],[23,30,34,60]], target = 3
输出:true

解题思路

这道题的核心是将二维矩阵「拉平」为一维有序数组,用标准二分查找解决:

  1. 矩阵特性 :由于每行升序且行首大于上一行行尾,整个矩阵本质是一个一维升序数组
  2. 坐标映射 :对于一维下标 mid,对应二维坐标为:
    • 行号:row = mid / nn 为矩阵列数)
    • 列号:col = mid % n
  3. 标准二分:用一维二分查找的逻辑,通过坐标映射访问矩阵元素,判断是否等于目标值。

Java 代码实现(标准二分版)

java

运行

复制代码
public class SearchMatrix {
    public boolean searchMatrix(int[][] matrix, int target) {
        if (matrix == null || matrix.length == 0 || matrix[0].length == 0) {
            return false;
        }

        int m = matrix.length;    // 行数
        int n = matrix[0].length; // 列数
        int left = 0;
        int right = m * n - 1;    // 一维数组的最大下标

        while (left <= right) {
            int mid = left + (right - left) / 2; // 避免溢出
            // 一维下标转二维坐标
            int row = mid / n;
            int col = mid % n;
            int num = matrix[row][col];

            if (num == target) {
                return true; // 找到目标值
            } else if (num < target) {
                left = mid + 1; // 目标在右半区
            } else {
                right = mid - 1; // 目标在左半区
            }
        }

        return false; // 未找到
    }
}

复杂度分析

  • 时间复杂度:O(log(mn)),等价于一维数组的二分查找,每次将搜索范围缩小一半。
  • 空间复杂度:O(1),仅使用常数级额外空间。

核心知识点总结

  1. 坐标映射公式row = mid / ncol = mid % n,是二维转一维的关键。
  2. 边界处理 :和一维二分完全一致,闭区间 [left, right],循环条件 left <= right
  3. 适用场景 :仅适用于行升序、行首大于上一行行尾的矩阵,若矩阵仅每行升序、列升序(如 LeetCode 240),需用其他方法。

二、在排序数组中查找元素的第一个和最后一个位置(LeetCode 34・中等)

题目描述

给你一个按照非递减顺序排列的整数数组 nums,和一个目标值 target。请你找出给定目标值在数组中的开始位置和结束位置。如果数组中不存在目标值 target,返回 [-1, -1]。你必须设计并实现时间复杂度为 O(logn) 的算法解决此问题。

示例:

plaintext

复制代码
输入:nums = [5,7,7,8,8,10], target = 8
输出:[3,4]

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

解题思路

这道题是二分查找的经典变形 ,核心是用两次二分分别查找左边界右边界

  1. 查找左边界 :找到第一个等于 target 的元素下标。
    • nums[mid] == target 时,不直接返回,而是收缩右边界 ,继续向左查找,直到 left 指向第一个等于 target 的位置。
  2. 查找右边界 :找到最后一个等于 target 的元素下标。
    • nums[mid] == target 时,不直接返回,而是收缩左边界 ,继续向右查找,直到 right 指向最后一个等于 target 的位置。
  3. 结果校验 :若左边界越界或不等于 target,说明数组中无 target,返回 [-1, -1],否则返回 [leftBoundary, rightBoundary]

Java 代码实现(两次二分版)

java

运行

复制代码
public class SearchRange {
    public int[] searchRange(int[] nums, int target) {
        int leftBoundary = findLeftBoundary(nums, target);
        int rightBoundary = findRightBoundary(nums, target);

        // 校验:左边界越界 或 左边界元素不等于target,说明无目标值
        if (leftBoundary == nums.length || nums[leftBoundary] != target) {
            return new int[]{-1, -1};
        }

        return new int[]{leftBoundary, rightBoundary};
    }

    /**
     * 查找target的左边界(第一个等于target的下标)
     */
    private int findLeftBoundary(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) {
                // 等于target时,收缩右边界,继续向左找
                right = mid - 1;
            } else {
                left = mid + 1;
            }
        }

        return left; // 循环结束,left指向第一个等于target的位置
    }

    /**
     * 查找target的右边界(最后一个等于target的下标)
     */
    private int findRightBoundary(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) {
                // 等于target时,收缩左边界,继续向右找
                left = mid + 1;
            } else {
                right = mid - 1;
            }
        }

        return right; // 循环结束,right指向最后一个等于target的位置
    }
}

复杂度分析

  • 时间复杂度:O(logn),两次二分查找,每次时间复杂度均为 O(logn)。
  • 空间复杂度:O(1),仅使用常数级额外空间。

核心知识点总结

  1. 左右边界查找逻辑
    • 左边界:nums[mid] >= target 时,right = mid - 1,最终 left 为左边界。
    • 右边界:nums[mid] <= target 时,left = mid + 1,最终 right 为右边界。
  2. 结果校验 :必须校验左边界是否越界、是否等于 target,避免数组中无 target 时返回错误结果。
  3. 边界处理 :闭区间写法,循环条件 left <= right,和标准二分完全一致。
相关推荐
Old Uncle Tom5 小时前
OpenClaw 记忆系统 -- 记忆预加载
java·数据结构·算法·agent
会编程的土豆6 小时前
洛谷题单入门1 顺序结构
数据结构·算法·golang
生信碱移6 小时前
PACells:这个方法可以鉴定疾病/预后相关的重要细胞亚群,作者提供的代码流程可以学习起来了,甚至兼容转录组与 ATAC 两种数据类型!
人工智能·学习·算法·机器学习·数据挖掘·数据分析·r语言
智者知已应修善业6 小时前
【51单片机中的打飞机设计】2023-8-25
c++·经验分享·笔记·算法·51单片机
智者知已应修善业8 小时前
【51单片机按键调节占空比3位数码管显示】2023-8-24
c++·经验分享·笔记·算法·51单片机
.5489 小时前
## Sorting(排序算法)
python·算法·排序算法
wuweijianlove9 小时前
算法的平均复杂度建模与性能回归分析的技术7
算法·数据挖掘·回归
子琦啊9 小时前
【算法复习】字符串 | 两个底层直觉,吃透高频题
linux·运维·算法
code_pgf11 小时前
Octo 算法详解-开源通用机器人策略模型技术报告
算法·机器人·开源
嘻嘻哈哈樱桃11 小时前
牛客经典101题题解集--动态规划
java·数据结构·python·算法·职场和发展·动态规划