LeetCode 744. 寻找比目标字母大的最小字母 | 从低效到最优的二分解法优化

在算法刷题中,二分查找是高频考点,而「寻找比目标字母大的最小字母」这道题(LeetCode 744)是二分查找的经典应用场景。本文将从初版低效解法 入手,分析问题所在,逐步优化到最优解法,帮你理解二分查找的核心逻辑和题目特性的利用技巧。

一、题目描述

原题链接

LeetCode 744. Find Smallest Letter Greater Than Target(https://leetcode.cn/problems/find-smallest-letter-greater-than-target/)

核心要求

给你一个字符数组 letters,该数组按非递减顺序 排列,以及一个字符 target。请你找出并返回 letters 中大于 target 的最小字符。如果不存在这样的字符,则返回 letters 的第一个字符。

示例

  • 示例 1:输入: letters = ["c","f","j"], target = "a",输出: "c"
  • 示例 2:输入: letters = ["c","f","j"], target = "c",输出: "f"
  • 示例 3:输入: letters = ["c","f","j"], target = "j",输出: "c"

优化解法:最优二分实现

1. 优化思路

  • 利用 letters 非递减的特性,去掉冗余去重,直接操作原数组,节省时间和空间;
  • 修正二分逻辑,精准瞄准「大于 target 的最小字符」;
  • 提前处理边界场景(target 大于所有字符),避免数组越界。
  1. 最优代码
java 复制代码
public char nextGreatestLetter(char[] letters, char target) {
    int left = 0;
    int right = letters.length - 1;
    
    // 提前处理边界:target大于/等于所有字符,直接返回第一个元素
    if (letters[right] <= target) {
        return letters[left];
    }
    
    // 二分查找:找大于target的最小字符
    while (left <= right) {
        // 防止left+right溢出,等价于(left+right)/2
        int mid = left + (right - left) / 2;
        if (letters[mid] > target) {
            // 当前元素大于target,尝试找更小的(左移右边界)
            right = mid - 1;
        } else {
            // 当前元素<=target,需要右移左边界,找更大的元素
            left = mid + 1;
        }
    }
    
    // 循环结束后,left指向第一个大于target的字符
    return letters[left];
}

核心逻辑解析

(1)边界预处理

if (letters[right] <= target) 直接判断「target 是否大于等于最后一个字符」,若是则返回第一个字符,避免后续二分越界。

(2)二分查找核心
  • 当 letters[mid] > target:说明 mid 位置的字符是候选,但可能有更小的符合条件的字符,因此左移右边界(right = mid - 1);
  • 当 letters[mid] <= target:说明 mid 位置的字符不符合要求,需要找更大的,因此右移左边界(left = mid + 1);
  • 循环结束时,left 必然指向「大于 target 的最小字符」(因为预处理已排除所有字符都 <=target 的情况)。
(3)时间 / 空间复杂度
  • 时间复杂度:O (logn)(纯二分查找,无额外遍历);
  • 空间复杂度:O (1)(仅用了几个变量,无额外数据结构)。

四、测试用例验证

用例 1:常规场景(target 小于所有字符)

输入:letters = ['c','f','j'], target = 'a'执行流程:

  • 边界判断:letters[2]='j' > 'a',进入二分;
  • 第一次循环:mid=1,letters[1]='f'>'a' → right=0;
  • 第二次循环:mid=0,letters[0]='c'>'a' → right=-1;
  • 循环结束,返回 letters[0]='c'(正确)。

用例 2:target 等于中间字符

输入:letters = ['c','f','j'], target = 'c'执行流程:

  • 边界判断:letters[2]='j' > 'c',进入二分;
  • 第一次循环:mid=1,letters[1]='f'>'c' → right=0;
  • 第二次循环:mid=0,letters[0]='c'<=c → left=1;
  • 循环结束,返回 letters[1]='f'(正确)。

用例 3:边界场景(target 等于最后一个字符)

输入:letters = ['c','f','j'], target = 'j'执行流程:

  • 边界判断:letters[2]='j' <= 'j' → 直接返回 letters[0]='c'(正确)。

用例 4:含重复元素场景

输入:letters = ['c','c','f','j'], target = 'c'执行流程:

  • 边界判断:letters[3]='j' > 'c',进入二分;
  • 第一次循环:mid=1,letters[1]='c'<=c → left=2;
  • 第二次循环:mid=2,letters[2]='f'>'c' → right=1;
  • 循环结束,返回 letters[2]='f'(正确)。

五、总结

  1. 二分查找的核心是精准匹配题目要求的判断条件,本题需严格区分「>=target」和「>target」,否则会导致结果错误;
  2. 解题时要充分利用题目给出的特性(如本题「非递减排列」),去掉冗余逻辑(如去重),提升效率;
  3. 边界条件是二分查找的高频坑点,提前预处理边界场景(如 target 大于所有字符),能有效避免数组越界。
相关推荐
倒头就睡的小比特3 天前
算法竞赛C++常用的STL
c++·算法
小羊没烦恼!3 天前
初探性能优化——2个月到4小时的性能提升
java·开发语言·windows·算法·c#
猎头南楼3 天前
知识社区推荐系统实践:新用户冷启动与长短期兴趣建模的挑战 资深推荐算法工程师
人工智能·深度学习·算法·机器学习
旖旎夜光3 天前
力控面试题 01.01: 判定字符是否唯一(位运算) —— 题解
c++·学习·算法·leetcode·力控
wzdark3 天前
大规模并行计算中的负载均衡算法研究4
算法
Because_of_Her13 天前
并查集-听课笔记
笔记·算法·并查集
码流子3 天前
高速公路安全监测实践:碰撞监测预警+物联网底座,从感知到处置的闭环
大数据·人工智能·物联网·算法·架构
another heaven3 天前
【算法/C++ MD5算法能否逆解码?原理、C++实现与同类哈希算法对比】
c++·算法·哈希算法
wzdark3 天前
从算法设计模式看编程思维的抽象能力4
算法
2601_962218613 天前
万象生鲜系统称重自动多退少补算法解决生鲜非标品痛点
大数据·数据库·人工智能·python·算法