一、题目
1、跳跃游戏(LC 55)
2、跳跃游戏2 (LC 45)
3、划分字母区间(LC 763)
二、题解
1、跳跃游戏(LC 55)

(1)分析
这道题判断能否从数组的第一个位置跳到最后一个位置,就是看每一步能覆盖到的最远范围。
可以维护一个变量表示当前能到达的最远下标,从头开始遍历每一个位置。在遍历过程中,如果当前位置已经超出了能到达的最远下标,那就说明根本跳不到这里,直接返回 false 就可以。如果能到达当前位置,就用当前位置加上能跳的长度,更新能到达的最远下标。只要一直遍历到数组结束,都没有出现无法到达的情况,就说明一定能跳到最后一位,返回 true 即可。过程中不纠结具体怎么跳,只关注最远覆盖范围。
时间复杂度是 O (n),只需要一次遍历数组。空间复杂度是 O (1),只用了几个变量记录状态,没有额外空间开销。
(2)题解
java
class Solution {
public boolean canJump(int[] nums) {
int maxIndex = 0; //可以到达的最大下标
for(int i = 0; i < nums.length; i++){
if(maxIndex < i){
return false; //到达不了当前位置,返回false
}
maxIndex = Math.max(maxIndex, i+nums[i]); //更新最大下标
}
return true; //循环结束,说明可以到达最大下标
}
}
2、跳跃游戏2 (LC 45)

(1)分析
这道题在跳跃游戏的基础上,要求算出跳到终点的最少步数,同样适用贪心算法,在当前能到达的范围内,尽可能跳到最远的地方,以此减少跳跃次数。
使用三个变量来完成整个逻辑,分别记录跳跃次数、当前这一步能到达的最远位置、下一步能到达的最远位置。遍历数组的时候,不断更新下一步能到达的最远位置,当遍历到当前这一步的边界时,就必须再跳一次,这时候把跳跃次数加一,并且把当前边界更新为下一步的最远位置。因为题目保证一定可以到达终点,所以只需要遍历到倒数第二个位置就够了。
时间复杂度是 O (n),一次遍历完成计算。空间复杂度是 O (1),仅使用常数变量。
(2)题解
java
class Solution {
public int jump(int[] nums) {
int ans = 0; //步数
int curEnd = 0; //当前的位置
int nextEnd = 0; //当前可以到达的最大位置
for(int i = 0; i < nums.length - 1; i++){
nextEnd = Math.max(nextEnd, i + nums[i]);
if(i == curEnd){ //此时需要再走一步,到达nextEnd
curEnd = nextEnd;
ans++;
}
}
return ans;
}
}
3、划分字母区间(LC 763)

(1)分析
这道题要把字符串划分成最多的片段,保证同一个字母只出现在一个片段里,最关键的思路就是先确定每个字母最后出现的位置,再用贪心的方式确定分割点。
先遍历一遍字符串,用数组记录下每一个字母最后一次出现的下标。然后再次遍历字符串,不断更新当前片段的结束位置,这个结束位置取当前字母最后出现位置的最大值。当遍历到当前片段的结束位置时,就说明这个片段里所有字母都不会再出现在后面了,此时可以完成一次分割,记录片段长度,再把起点设为下一个位置继续遍历。遍历结束后返回 ans 结果。
时间复杂度是 O (n),只需要两次线性遍历字符串。空间复杂度是 O (1),记录最后位置的数组大小固定,属于常数级空间。
(2)题解
java
class Solution {
public List<Integer> partitionLabels(String s) {
List<Integer> ans = new ArrayList<>(); //结果集合
char[] chs = s.toCharArray();
int n = chs.length;
int[] last = new int[26]; //每个字符最后出现的位置
for(int i = 0; i < n; i++){
last[chs[i] - 'a'] = i; //记录每个字符的最后下标
}
int start = 0; //闭区间
int end = 0;
for(int i =0; i <n; i++){
end = Math.max(last[chs[i] -'a'], end); //更新最大下标
if(i == end){ //此时可以分割
ans.add(end - start + 1);
start = end + 1;
}
}
return ans;
}
}