LeetCode 55 - 跳跃游戏 - 解题思路记录(递归+动态规划法) - Golang

1,770 阅读4分钟

Hello, 现在是凌晨2点, 刚刚结束完一天的工作,忙着把算法题写完.那么今天继续讲解算法题第55题, 也是一道经典的面试题目,那我们来一起看一看吧.

Given an array of non-negative integers, you are initially positioned at the first index of the array.

Each element in the array represents your maximum jump length at that position.

Determine if you are able to reach the last index.

Example1

Input: nums = [2,3,1,1,4]
Output: true
Explanation: Jump 1 step from index 0 to 1, then 3 steps to the last index.

Example2

Input: nums = [3,2,1,0,4]
Output: false
Explanation: You will always arrive at index 3 no matter what. Its maximum jump length is 0, which makes it impossible to reach the last index.

来简单描述一下这道题目的意思, 大致上就是给定一个数组, 数组中的元素代表你可以往后跳的一个步数,比方nums[0]=2 代表它可以选择跳到nums[1] 和 nums[2] 这两步

我们来画个图,举个例子给大家看看

我用四种不同的颜色代表每个元素可以跳跃的步数, 这下子是不是就很清楚了

接着我们来回顾一下递归动态规划的思想

  1. 创建一个数组 存储动态规划的过程
  2. 寻找递归结束的条件(可以先画个树图出来帮助理解)

我们先来看一下 动态规划我们要准备什么? 其实我们就是要保存, 每个点是否能跳跃到终点的记录

我们用-1,0,1来代表3种状态,分别是不能到达,初始化,可以到达三个阶段

我们先初始化dp的数组, 并且将终点设置为1, 因为终点肯定能到终点 接下来,我们来思考一下

递归的结束条件应该是什么?

我们先把递归图画出来

从这道例子中, 我们递归树应该是这样子

我们的递归顺序,就是从左边的1开始,往右边数,最后才做2自己本身, 在这个例子中你会发现, 你无论选那一条路 你都能到终点对吧, 那我们就换一题来讲

我们对着下面这个题目, 画一下动态规划表

所以, 我们的动态规划表应该是上图那样的结果. 1代表可到达的路径, -1代表不可到达

所以说, 我们递归结束的条件 应该是 dp[i]==-1dp[i]==1 为结束条件

如果 dp[i]==0 说明该节点尚未被计算,需要再次把这个节点进行递归 如果递归到了死胡同了, 我们就把该点标记成为-1退出则可

让我们来看一下递归代码

func recurrentJump(position int,nums []int, dp *[]int) bool{
	if (*dp)[position] == 1{
		return true
	}else if (*dp)[position] == -1{
		return false
	}
	maxJump := min(position + nums[position], len(nums)-1)
	for i:= position + 1; i<=maxJump; i++{
		result:=recurrentJump(i, nums, dp)
		if result {
			(*dp)[position] = 1
			return true
		}
	}
	(*dp)[position] = -1
	return false
}
func min(x int, y int) int{
	if (x < y){
		return x
	}
	return y
}

递归的主函数就在上面, 和我们描述的一致, 如果说dp[i]==-1dp[i]==1直接可以返回否则说明该节点尚未被计算

我们来看一下节点未被计算的时候需要做的循环

maxJump := min(position + nums[position], len(nums)-1)
for i:= position + 1; i<=maxJump; i++{
	result:=recurrentJump(i, nums, dp)
	if result {
		(*dp)[position] = 1
		return true
	}
}

首先 求最小的跳跃 次数 这里的position + nums[i]nums长度比较,取小的一位代表着该节点,最大能跳到哪里 然后我们再循环递归, 把该节点能跳的位置, 全部再递归一次

如果说返回值等于 true 则代表本节点可以跳跃到终点, 记录本节点为 dp[position]=1 然后返回 true

告诉上一层的节点, 你也可以通过我到达终点, 如下图所示

然后我们在看一下主函数

func canJump(nums []int) bool {
	var dp = make([]int, len(nums))
	for i:=0; i<len(nums); i++ {
		dp[i]  = 0
	}
	dp[len(nums)-1] = 1
	ans:= recurrentJump(0, nums, &dp)
	return ans
}

这里, 没什么好讲的 就是初始化DP数组, 然后从下标0开始递归,

这就是今天递归+DP的算法,性能不高, 明天会继续写一个其他的解法

其他相关题目连接

LeetCode 53 - 最大子序和 - 解题思路记录(附带动态规划) - GoLang

LeetCode 49 - 字母异位词分组 - 解题思路记录 - Golang

LeetCode 21 - 合并有序链表 - 解题思路记录 - GoLang

LeetCode 2 - 两数之和 - 解题思路记录 - GoLang

LeetCode 12 - 罗马数字 - 解题思路记录 - GoLang

LeetCode 15 - 3数之和 - 解题思路记录 - GoLang

LeetCode 5 - 回文串 - 解题思路记录 - GoLang