【忍者算法】探秘数组搜索:玩转二分查找的经典应用!|LeetCode 34「在排序数组中查找元素的第一个和最后一个位置」

探秘数组搜索:玩转二分查找的经典应用!

大家好,我是忍者算法。今天我要和大家分享一道非常经典的二分查找题目 - LeetCode 34「在排序数组中查找元素的第一个和最后一个位置」。这道题看似简单,实则暗藏玄机,是理解二分查找边界处理的绝佳材料。

📚 从生活场景说起

想象你在整理一叠按时间顺序排好的照片,其中有多张是同一天拍的。如果要找出某一天最早和最晚拍的那张照片,你会怎么做?高效的方法是先用二分找到这一天的任意一张照片,然后再分别向左右寻找边界。这正是我们今天要解决的问题的生活映射!

💡 问题解析

题目要求
给定一个按升序排列的整数数组 nums,和一个目标值 target。找出给定目标值在数组中的开始位置和结束位置。如果数组中不存在目标值,返回 [-1, -1]。

示例

输入:nums = [5,7,7,8,8,10], target = 8
输出:[3,4]  // 8在位置3和位置4出现

输入:nums = [5,7,7,8,8,10], target = 6
输出:[-1,-1]  // 6不存在于数组中

🤔 思路发展历程

让我们看看解决这个问题时,思维是如何层层递进的:

1. 朴素思路

最直观的方法是遍历一遍数组,记录第一次和最后一次出现的位置。但这种方法的时间复杂度是O(n),没有利用数组已排序的特性。

2. 二分查找思路

既然数组已排序,我们可以用二分查找将时间复杂度优化到O(log n)。关键在于设计两个二分查找:一个找左边界,一个找右边界。

🚀 优雅的解决方案

class Solution {
    public int[] searchRange(int[] nums, int target) {
        // 特判:空数组的情况
        if (nums == null || nums.length == 0) {
            return new int[]{-1, -1};
        }
        
        // 分别查找左右边界
        int leftBorder = findBorder(nums, target, true);
        int rightBorder = findBorder(nums, target, false);
        
        return new int[]{leftBorder, rightBorder};
    }
    
    // 查找边界的统一函数,isLeft为true时查找左边界,false时查找右边界
    private int findBorder(int[] nums, int target, boolean isLeft) {
        int left = 0;
        int right = nums.length - 1;
        
        while (left <= right) {
            int mid = left + (right - left) / 2;
            
            if (nums[mid] == target) {
                // 找到目标值时的处理
                if (isLeft) {
                    // 查找左边界时,收缩右边界
                    if (mid == 0 || nums[mid - 1] != target) {
                        return mid;
                    }
                    right = mid - 1;
                } else {
                    // 查找右边界时,收缩左边界
                    if (mid == nums.length - 1 || nums[mid + 1] != target) {
                        return mid;
                    }
                    left = mid + 1;
                }
            } else if (nums[mid] < target) {
                left = mid + 1;
            } else {
                right = mid - 1;
            }
        }
        
        return -1;  // 没找到目标值
    }
}

📝 代码详解

让我们深入理解这个优雅的解决方案:

1. 整体架构

我们设计了一个统一的边界查找函数,通过布尔参数控制是查找左边界还是右边界。这种设计既减少了代码重复,又让逻辑更加清晰。

2. 边界查找的精妙之处

当找到目标值时,我们并不立即返回,而是:

  • 查找左边界时,我们要确认前一个数不是目标值
  • 查找右边界时,我们要确认后一个数不是目标值
    这样就能精确定位边界位置。

3. 条件判断的艺术

代码中的边界检查(mid == 0 或 mid == nums.length - 1)确保了我们不会发生数组越界。这些细节体现了代码的健壮性。

🎯 易错点剖析

  1. 返回值处理

    • 必须先判断数组为空的情况
    • 当目标值不存在时,要返回[-1, -1]
  2. 边界条件

    • 别忘了检查数组首尾的特殊情况
    • 当找到目标值时,不要急于返回
  3. 循环终止条件

    • while循环的条件是 left <= right
    • 这确保了不会漏掉单个元素的情况

💡 举一反三

这道题的思路可以延伸到很多场景:

  1. 查找最后一个小于目标值的位置

    • 只需稍微修改边界判断条件
  2. 查找第一个大于目标值的位置

    • 类似的二分思路,不同的判断条件
  3. 统计目标值的出现次数

    • 可以用右边界减去左边界再加1

🌟 面试技巧

  1. 展示思维过程

    • 先说明暴力解法,再优化到二分
    • 体现你的算法思维能力
  2. 代码优化意识

    • 展示代码复用和模块化的能力
    • 注意代码的可读性和维护性
  3. 考虑周全

    • 主动提及边界情况的处理
    • 展示你考虑问题的全面性

作者:忍者算法
公众号:忍者算法
🎁 回复【刷题清单】获取LeetCode高频面试题合集
🧑‍💻 回复【代码】获取多语言完整题解
💡 回复【加群】加入算法交流群,一起进步
#算法面试 #LeetCode #二分查找 #数组

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

忍者算法

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值