Jump Game II - LeetCode
大概翻译一下题目的意思,有一个非负整数数组,数组某个index上的数值代表在这个index上能跳的最大步数,要从这个数组的第一个位置跳到最后一个位置,求跳动的最少次数。
例:A = [2,3,1,1,4]
从A[0]开始跳,A[0]=2,所以在这个位置最多能跳2步。
如果跳两步那么就到了A[2],而A[2]=1所以在这个位置只能跳1步
显然这个样例的答案是2,在A[0]跳1步,到达A[1],再从A[1]跳3步到达数组最后一个位置
先写一下我的2B方法
题目要求最少的次数,显然可以用BFS做
具体的做法是用一个队列存储当前的位置,然后把从这个位置能到达的所有其他位置按顺序(按什么顺序视情况而定,后面会提到)压入队列,
另外再创建一个队列用来记录到达当前位置已用的步数
这样已经可以得到正确答案了,但是效率不高,所以提交的时候超时了,因为很多节点会被重复考虑,举个例子
比如B=[2,2,2,1,4],到达B[2](注意是B[2])有两种方法:
1.先跳1步,再跳1步,共跳2次
2.跳两步,共跳1次
显然这里只需要考虑第2种方法就行,因为不论用哪种方法,到达B[2]后,接下来的情况是相同的,而第一种方法已经跳了2次,最后的跳动次数一定会比第二种方法多。
所以要去掉这种重复冗余的情况,去掉的方法就是用一个set存储已经经过的节点,当再次到达该节点时就忽略。
为什么这样就能起作用?因为在做BFS的时候,某个index可能会被到达多次,但是第一次到达该index所用的跳动次数一定是最少的
优化之后过了91/92个测试,就差了1个。。
这时候就要考虑顺序了,,我一开始是按步数递增的顺序把下一个index压入队列的。这样做的结果是,每次都优先考虑了跳一步的情况,,所以会很慢,,,于是改了一下顺序,按步数递减的顺序把下一个index压入队列,这样就会优先考虑跳动步数最大的情况。
#include <iostream>
#include <vector>
#include <queue>
#include <set>
using namespace std;
class Solution {
public:
int jump(vector<int>& nums) {
int n = nums[0], p = 0, i = 0, steps = 0;
set<int> s;
queue<int> pos;
queue<int> step;
s.insert(0);
pos.push(0);
step.push(0);
while (!pos.empty() && p < nums.size()-1) {
p = pos.front(); pos.pop();
steps = step.front(); step.pop();
steps++;
for (i = nums[p]; i >= 1; i--) { //这里改动了顺序
if (p + i == nums.size()-1) break;
if (s.find(p+i) == s.end()) {
pos.push(p+i);
step.push(steps);
s.insert(p+i);
}
}
if ((p + i == nums.size()-1) && (i != nums[p]+1)) break;
}
return steps;
}
};
结果不是太好,看了别人的方法才知道可以把两个队列合并成1个队列,用pair就行。。这样就会少很多次pop,push操作
下面是效率最高的做法
class Solution {
public:
int jump(vector<int>& nums) {
int cnt = 0;
int maxidx = 0;
int curidx = 0;
for(int i=0; i<nums.size()-1; i++){
maxidx = max(maxidx, nums[i]+i);
if(i==curidx){
cnt++;
curidx = maxidx;
}
}
return cnt;
}
};
代码很简洁,仔细去理解一下这个代码会发现其实这个方法有些投机的成分,为什么这么说?
题目里最后有个note说,“你可以认为你永远能够到达最后一个index”,大多数人会认为这个条件是题目的一个简化,可以让我们少考虑一些情况,但是这个解法重分利用了这个条件——“你总是能够到达最后一个index”。
这解法的思路是“尽可能地走往后走”,反正一定能到达最后一个index,那么只要尽可能往后走就一定能以最少的步数到达最后的index。