LeetCode/跳跃游戏
给定一个非负整数数组nums,你最初位于数组的第一个下标 。
数组中的每个元素代表你在该位置可以跳跃的最大长度。
判断你是否能够到达最后一个下标。
1. 回溯法
纯粹当做练手,运行不仅超时,还超内存
使用递归的方式,从当前位置做选择,选择为当前位置能跳的距离,然后转换到下一位置,到达目的结束递归
回溯法
class Solution {
public:
bool flag = false; //设定初始判断值为否
bool canJump(vector<int>& nums) {
trackback(nums,0);//进入递归寻路
return flag; //返回结果
}
void trackback(vector<int>& nums,int start){
if(start==nums.size()-1){ //边界条件,如果达到最后位置,则取真
flag=true; return;
}
if(flag==true||nums[start]==0) return;//减少递归运算
for(int i=start+1;i<=start+nums[i];i++){
trackback(nums,i);//从能跳的长度中做选择
//出递归,相当于撤销选择
}
}
};
2. 贪心算法
记录当下一跳的范围,下一次在该范围中遍历,得到下一跳的范围
当某一跳范围大于数组范围,为真,若下一跳范围边界没有发生改变,则为假,性能为线性
class Solution {
public:
bool canJump(vector<int>& nums) {
int buff;
int maxpos = 0; //记录当前跳跃最远距离
int start = 0; //记录当前跳跃开始位置
while(true){
buff = maxpos; //缓存上一跳范围
for(int i = start;i<=buff;i++){//在当前范围内遍历,算出下一跳范围
maxpos = max(maxpos,i+nums[i]); //计算当前一跳最远位置
}
if(maxpos>=nums.size()-1) return true; //若能达到最后一个位置,为真
else if(maxpos==buff) return false; //下一跳范围不变,为假
start = buff+1; //下一跳开始位置为上一跳极限位置的后一位
}
}
};
官方写法直接顺序推进,遍历范围为初始位置到维护的一个最大位置,
最大位置在遍历的时候实时更新,若遍历结束没有达到末端则返回假
每次遍历都做一次判断,而自己写的算法每一跳范围做一次判断,故官方写法耗时更长
官方写法
class Solution {
public:
bool canJump(vector<int>& nums) {
int n = nums.size();
int rightmost = 0;
for (int i = 0; i < n; ++i) { //顺序遍历,维护一个最远位置即可
if (i <= rightmost) { //
rightmost = max(rightmost, i + nums[i]); //算出当下最远位置
if (rightmost >= n - 1) {//判断该最远位置是否满足要求
return true;
}
}
}
return false; //遍历完也没满足即返回假
}
};
更简洁的写法
class Solution {
public:
bool canJump(vector<int>& nums) {//简洁但性能很拉
int k = 0;
for (int i = 0; i < nums.size(); i++) {//从前往后全遍历,直接判断是否超过最大范围
if (i > k) return false;
k = max(k, i + nums[i]); //更新最大范围
}
return true;
}
};
3. 动态规划
将问题规模不断缩小,终点位置的可达性转换为前面某个位置的可达性,直至转换到起点,证明可达
class Solution {
public:
bool canJump(vector<int>& nums) {
int n = nums.size();
int end = n-1; //当前终点位置
for(int i =end -1;i>=0;i--){ //从终点前一位置从后往前遍历
if(end-i<=nums[i]) end = i; //如果当前节点到终点距离符合,则转移终点
}
return end==0;//将终点转移至起点,说明路径可达
}
};