背包 (4000 ms):
这个题,如果用背包的思路来做的话,则用一维列表来记录对应位置是否可到达,如 dp[4] 表示是否可以从起点 (索引0) 到达索引4的位置
class Solution(object):
def canJump(self, nums):
"""
:type nums: List[int]
:rtype: bool
"""
dp = [False for _ in range(len(nums))]
dp[0] = True
for loc, limit in enumerate(nums[:-1]):
if dp[loc]:
# 判断当前位置是否可到达
for pace in range(1, limit + 1):
# 枚举跳跃距离
if loc + pace < len(nums) - 1:
dp[loc + pace] = True
else:
# 剪枝:当前位置可直接到达终点
return True
return dp[-1]
假设最坏情况下数组中每个值都接近其长度,那么这个算法的最坏时间复杂度就是O(),在不做剪枝的情况下是过不了测试的
动态规划 (100 ms):
另一种动态规划的思路是,用一维列表来表示对应位置可移动的最远距离。状态转移方程如下:
dp[idx] = max([ dp[idx - 1] - 1, num[idx] ])
然后以 [3, 2, 1, 0, 4] 为样例,推导一下边界条件即可
class Solution(object):
def canJump(self, nums):
"""
:type nums: List[int]
:rtype: bool
"""
for idx in range(1, len(nums)):
state_1 = nums[idx - 1] - 1
state_2 = nums[idx]
if state_1 >= 0:
# 该位置可到达
nums[idx] = max([state_1, state_2])
else:
return False
if len(nums) <= 1 or nums[-2]:
return True
else:
return False
相比背包的做法,时间复杂度已经降到了O(n),但是测试结果还是不太理想
贪心 (65 ms):
思路很简单,直接看代码的注释吧
class Solution(object):
def canJump(self, nums):
"""
:type nums: List[int]
:rtype: bool
"""
max_idx = 0
# 可到达的最远点索引
target = len(nums) - 1
# 终点的索引
for loc, pace in enumerate(nums):
if max_idx >= loc:
# 确认当前点 (索引loc) 可到达
max_dist = max([loc + pace, max_idx])
# 比较取最优
if max_dist >= target:
return True
return False
我觉得吧,在很多情况下,无论是时间复杂度还是空间复杂度,贪心都是优于动态规划的。但是感觉贪心有点玄乎,得多练练了