LeetCode 面试经典 150 题之最后一个单词的长度:逆向遍历高效解法
在 LeetCode 面试经典 150 题中,“最后一个单词的长度” 是一道看似简单却容易踩坑的字符串处理题。它主要考察我们对字符串边界条件(如末尾空格、单个单词、全空格字符串)的处理能力,以及是否能想到更高效的遍历方式,是面试中筛选基础编程素养的常见题目,掌握其核心思路能帮助我们快速应对类似字符串处理场景。
一、题目概览与链接
首先明确题目要求:给你一个字符串 s,由若干单词组成,单词前后用一些空格字符隔开。返回字符串中最后一个单词的长度。其中,“单词” 是指仅由字母组成、不包含任何空格字符的最大子字符串。
题目链接:LeetCode 58. 最后一个单词的长度(点击链接可直接跳转至题目页面,建议先自行尝试解题,尤其注意处理末尾空格、全空格等特殊情况)
二、解题思路深度剖析
拿到这道题,很多人第一反应是 “从左到右遍历,记录每个单词的长度,最后返回最后一个单词的长度”。但这种思路需要处理 “单词间空格”“末尾空格” 等问题,容易遗漏边界条件。而从字符串末尾逆向遍历的思路,能直接聚焦 “最后一个单词”,跳过不必要的空格处理,效率更高且逻辑更简洁。
1. 核心思路:逆向定位,跳过末尾空格
核心逻辑是 “先找最后一个单词的结尾,再找它的开头”—— 因为最后一个单词的结尾一定是字符串中 “从末尾数第一个非空格字符”,而开头则是 “从结尾往回数第一个空格字符的下一位”,两者的索引差就是单词长度。具体拆解为两个关键步骤:
步骤 1:定位最后一个单词的结尾(标记为 right)
从字符串末尾开始向前遍历,跳过所有连续的空格字符,直到找到第一个非空格字符。这个字符就是最后一个单词的最后一个字符,其索引记为 right。
- 为什么要先跳末尾空格?因为题目中字符串可能以多个空格结尾(如 s = "Hello World "),这些空格不属于任何单词,必须先排除,才能找到真正的单词结尾。
步骤 2:定位最后一个单词的开头(标记为 left)
从 right 位置继续向前遍历,跳过所有连续的非空格字符(即最后一个单词的字符),直到找到第一个空格字符(或遍历到字符串开头)。这个空格字符的索引记为 left(若遍历到开头仍无空格,left 记为 -1,表示单词从索引 0 开始)。
步骤 3:计算单词长度
最后一个单词的长度 = right - left。因为 left 是单词开头的前一个位置(空格或字符串外),right 是单词结尾位置,两者相减刚好是单词的字符个数。
2. 具体步骤拆解(附示例)
为了让思路更直观,我们用三个典型示例演示逆向遍历的过程,覆盖常见场景:
示例 1:正常场景(末尾无空格)
s = "Hello World"(长度为 11)
- 步骤 1:从索引 10(字符 'd')开始,因是非空格,直接定位 right = 10。
- 步骤 2:从 10 向前遍历,依次经过 'l'(9)、'r'(8)、'o'(7)、'W'(6),到索引 5 时遇到空格,定位 left = 5。
- 步骤 3:长度 = 10 - 5 = 5(“World” 的长度为 5,正确)。
示例 2:末尾有多个空格
s = " fly me to the moon "(末尾有 2 个空格,总长度 20)
- 步骤 1:从索引 19(空格)向前跳,18 也是空格,到 17 时是 'n'(非空格),定位 right = 17。
- 步骤 2:从 17 向前遍历,直到索引 11 时遇到空格(“the” 后的空格),定位 left = 11。
- 步骤 3:长度 = 17 - 11 = 6(“moon” 的长度为 6,正确)。
示例 3:全空格或单个单词
- 场景 A(全空格):s = " "
-
- 步骤 1:从末尾向前遍历,所有字符都是空格,最终 right 未找到非空格字符(可初始化为 -1),直接返回 0(无单词,正确)。
- 场景 B(单个单词):s = "Hello"
-
- 步骤 1:从索引 4('o')定位 right = 4。
-
- 步骤 2:向前遍历到索引 0('H')仍无空格,继续到 - 1(字符串开头外),定位 left = -1。
-
- 步骤 3:长度 = 4 - (-1) = 5(“Hello” 的长度为 5,正确)。
3. 关键注意点
- 边界条件处理:必须考虑 “全空格字符串”(返回 0)、“单个单词无空格”(left = -1)、“末尾多个空格”(跳过空格找 right)这三种核心场景,否则容易出现错误。
- 遍历终止条件:逆向遍历时要注意索引不能小于 0(避免数组越界),比如在找 left 时,若遍历到索引 0 仍无空格,需停止并将 left 设为 -1。
- 效率优势:逆向遍历无需遍历整个字符串(如示例 2 中,只需遍历末尾部分空格和最后一个单词),但最坏情况下(如全非空格字符串)仍需遍历全部字符,时间复杂度仍为 O (n),属于最优解。
三、复杂度分析
1. 时间复杂度:O (n)
- 其中 n 是字符串 s 的长度。
- 逆向遍历过程中,我们最多遍历整个字符串一次(如全非空格字符串:先从末尾到开头找 right(实际只需 1 步),再从 right 到开头找 left(遍历 n 步),总步数为 n+1,仍为 O (n))。所有操作均为常数时间判断,因此时间复杂度为 O (n),效率最优。
2. 空间复杂度:O (1)
- 整个算法仅使用了 left、right 两个索引变量(或额外一个循环变量),空间占用不随字符串长度变化,始终为常数级别。因此空间复杂度为 O (1),属于 “原地算法”,内存使用高效。
四、代码实现(多语言版本)
根据上述思路,我们实现多语言代码,重点处理边界条件(如全空格、索引越界),代码简洁且易读:
1. Python 代码
class Solution:def lengthOfLastWord(self, s: str) -> int:right = len(s) - 1while right >= 0 and s[right] == ' ':right -= 1left = rightwhile left >= 0 and s[left] != ' ':left -= 1return right - left
2. Java 代码
class Solution {public int lengthOfLastWord(String s) {int n = s.length();int right = n-1;while (right >= 0 && s.charAt(right) == ' '){right--;}int left = right;while (left >= 0 && s.charAt(left) != ' '){left--;}return right-left;}
}
五、总结与拓展
“最后一个单词的长度” 这道题的核心启示是:面对 “找最后一个目标” 的问题,逆向遍历往往比正向遍历更高效。正向遍历需要记录所有单词的长度,还要处理中间空格,而逆向遍历能直接聚焦目标,减少不必要的操作,同时避免遗漏边界条件。
这道题虽然难度较低,但能很好地锻炼我们 “选择最优遍历方向” 和 “处理边界条件” 的能力 —— 而这正是面试中考察的核心素养。希望通过本文的分析,大家不仅能掌握这道题的解法,更能学会在类似问题中 “逆向思维”,提升解题效率。
如果在代码实现或思路理解上有疑问,欢迎在评论区留言讨论,一起攻克字符串处理的常见问题!