题目描述
LeetCode 58. Length of Last Word(最后一个单词的长度)
给你一个字符串 s
,由若干单词组成,单词前后用一些空格字符隔开。返回字符串中最后一个单词的长度。
单词是指仅由字母组成、不包含任何空格字符的最大子字符串。
示例
示例 1:
输入:s = "Hello World"
输出:5
解释:最后一个单词是 "World",长度为 5。
示例 2:
输入:s = " fly me to the moon "
输出:4
解释:最后一个单词是 "moon",长度为 4。
示例 3:
输入:s = "luffy is still joyboy"
输出:6
解释:最后一个单词是 "joyboy",长度为 6。
约束条件
1 <= s.length <= 10^4
s
仅由英文字母和空格' '
组成s
中至少存在一个单词
解法对比分析
解法一:字符串分割法
class Solution {
public int lengthOfLastWord(String s) {
String[] words = s.split(" ");
int length = words.length;
return words[length-1].length();
}
}
算法思路
- 使用
split(" ")
方法将字符串按空格分割成字符串数组 - 获取数组的最后一个元素(即最后一个单词)
- 返回最后一个单词的长度
执行流程示例
以输入 " fly me to the moon "
为例:
步骤 | 操作 | 结果 |
---|---|---|
1 | s.split(" ") |
["", "", "", "fly", "me", "", "", "to", "", "", "the", "moon"] |
2 | words.length |
12 |
3 | words[11] |
"moon" |
4 | words[11].length() |
4 |
说明: 根据 Java String 的 split(String regex)
方法文档,当使用单参数的 split()
方法时,尾部的空字符串会被自动丢弃。因此,即使字符串末尾有空格,也不会在结果数组的末尾产生空字符串,可以直接获取数组的最后一个元素作为最后一个单词。
算法特点
- 优点: 代码简洁,一行即可解决
- 缺点: 仍会在中间产生空字符串(连续空格的情况),但不影响获取最后一个单词
复杂度分析
- 时间复杂度: O(n)
split()
操作:O(n)- 总体:O(n)
- 空间复杂度: O(n)
- 创建字符串数组存储所有单词
解法二:反向遍历法(推荐)
class Solution {
public int lengthOfLastWord(String s) {
int index = s.length() - 1;
// 跳过末尾的空格
while(index >= 0 && s.charAt(index) == ' '){
index--;
}
int length = 0;
// 计算最后一个单词的长度
while(index >= 0 && s.charAt(index) != ' '){
length++;
index--;
}
return length;
}
}
算法思路
- 从字符串末尾开始向前遍历
- 先跳过所有末尾的空格字符
- 然后统计连续的非空格字符个数(即最后一个单词的长度)
- 遇到空格或到达字符串开头时停止
执行流程示例
以输入 " fly me to the moon "
为例:
步骤 | index |
s.charAt(index) |
操作 | length |
---|---|---|---|---|
初始 | 23 | ' ' |
跳过空格 | 0 |
1 | 22 | ' ' |
跳过空格 | 0 |
2 | 21 | 'n' |
开始计数 | 1 |
3 | 20 | 'o' |
计数 | 2 |
4 | 19 | 'o' |
计数 | 3 |
5 | 18 | 'm' |
计数 | 4 |
6 | 17 | ' ' |
遇到空格,结束 | 4 |
复杂度分析
- 时间复杂度: O(n)
- 最坏情况下需要遍历整个字符串
- 但实际上通常只需要遍历最后一个单词和末尾空格的长度
- 空间复杂度: O(1)
- 只使用了常数个额外变量
两种解法对比
对比维度 | 解法一(分割法) | 解法二(反向遍历法) |
---|---|---|
时间复杂度 | O(n) | O(n) |
空间复杂度 | O(n) | O(1) |
代码简洁度 | 非常简洁 | 稍长但逻辑清晰 |
内存使用 | 需要存储所有单词 | 只用几个变量 |
边界处理 | split() 自动处理末尾空格 | 手动处理各种情况 |
性能表现 | 较差(创建数组开销大) | 较好(原地操作) |
推荐指数 | ⭐⭐⭐⭐ | ⭐⭐⭐⭐⭐ |
特殊情况分析
只有一个单词
输入: "hello"
解法一结果: 5 ✓
解法二结果: 5 ✓
单词间多个空格
输入: "a b"
解法一结果: 1 ✓
解法二结果: 1 ✓
首尾都有空格
输入: " hello world "
解法一结果: 5 ✓
解法二结果: 5 ✓
优化建议
解法一优化
public int lengthOfLastWord(String s) {
String[] words = s.trim().split("\\s+"); // 使用正则表达式处理多个空格
return words[words.length - 1].length();
}
解法二优化(更简洁的写法)
public int lengthOfLastWord(String s) {
int end = s.length() - 1;
// 跳过末尾空格
while (end >= 0 && s.charAt(end) == ' ') end--;
// 找到单词开始位置
int start = end;
while (start >= 0 && s.charAt(start) != ' ') start--;
return end - start;
}
总结
对于 LeetCode 58 题,两种解法各有优势:
解法一(字符串分割法):
- ✅ 代码极简:一行代码解决问题
- ✅ 边界处理自动:
split()
方法自动丢弃尾部空字符串 - ❌ 空间开销大:需要创建字符串数组
解法二(反向遍历法):
- ✅ 空间效率高:O(1) 空间复杂度
- ✅ 性能更好:避免数组创建开销
- ✅ 面试友好:展示算法思维
- ❌ 代码稍长:需要手动处理边界
推荐建议:
- 简单场景或代码竞赛:优先选择解法一(简洁高效)
- 追求极致性能或技术面试:推荐解法二(展示技术深度)
这道题很好地展示了不同解法的权衡取舍,以及Java API 使用的细节知识。