leetcode刷题_(arr,Kadane Algorithm,DP,Greedy)_code55/45(跳子游戏1/2)

code55: Jump Game

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.

Example 1:

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

Input: [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.

解答:
1 首先想到的是回溯法 也就是类似枚举然后不成功抛弃。
从左到右,如果能到右边某一位,则右边继续分裂。直到出现能到达最右的分支为止,将global变量设置为True。(为了防止多次复跳,在某一次到达该点时将其值变为0,这样后面到的就会停下来。)
理论上是行得通的,止步于TLE…
代码:

class Solution:#TLE
    def canJump(self, nums) -> bool:

        global judge
        judge = False
        i = 0
        print(len(nums), '#')
        # for i in range(len(nums-1)):
        #print(len(nums))

        self.jump(nums, i)


        return judge

    def jump(self, arr, start):
        global judge
        while not judge:
            #print(start)
            if start < len(arr)-1 and arr[start]==0:

                return
            elif arr[start]>=len(arr)-start-1:
                judge = True
                return

            elif start > len(arr) - 1:
                return
            else:
                x = arr[start]
                arr[start] = 0
                for i in range(1, x + 1):
                    self.jump(arr, start + i)

2 寻找更好的方法:
答案解析提供的解答中 运用了DP的思想。
从右到左,如果能到good点的将其标记为good,如果到不了,则标记为bad,遍历到最左边,然后判断第一个点是否为good。
我的解答中采用新数组记录的方法记录每个点的好坏,0坏1好。
最终依然止步于TLE…

class Solution3:#TLE    DP
    def canJump(self,nums):
        l = len(nums)
        new_arr = [1]
        for i in range(1,l):
            print(i)
            #print(new_arr,l-i-1)
            x = nums[l-i-1]
            if x == 0:
                new_arr.append(0)
            else:

                l2 = len(new_arr)
                start = max(l2-x,0)
                temp = max(new_arr[start:])
                if temp>0:
                    new_arr.append(1)
                else:
                    new_arr.append(0)
        if new_arr[-1] == 1:
            return True
        else:
            return False

时间复杂度分析为O(N^N),理论上应该没问题的。

3 再度寻找更好的方法:
答案给了个 greedy的解法。
提到了 i+nums[i]与len(nums)-1的大小关系。也就是说满足左边大的时候就是好点。
然后评论区也给出了优化:
应该从左往右遍历(遍历的范围是动态的,根据现存的最大值决定),如果发现最大值够不到len-1,则返回Flase。
采用while循环,如果跳出循环证明没有到达边界。循环内部设置一旦发现最大值大于边界则返回True。

代码:

class Solution2:#KA + DP
    def canJump(self, nums) -> bool:
        max_len = 0
        i=0
        while i <=max_len:
            max_len = max(max_len,i+nums[i])
            if max_len >=len(nums)-1:
                return True
            i+=1
        return False

这个方法代码极其优雅。需要学习

最后根据这个导向,我想到了之前提到的Kadane’s Algorithm,也就是将当前的最大值存储在nums[i-1]中,然后使用nums[i]+1做比较,把最大值放在Nums[i]中。如果出现最大值大于边界则返回True。如果遍历边界超出了最大值则跳出遍历。

代码:

class Solution4:#KA
    def canJump(self, nums):

        if len(nums) == 1:
            return True
        elif nums[0] == 0:
            return False
        else:

            i = 1
            #max_value = nums[0]
            while i <= nums[i-1]:
                nums[i] = max(nums[i - 1], i + nums[i])
                #print(i,nums)
                if nums[i] >= len(nums) - 1:
                    return True
                i += 1
            return False

接下来进入第二题:
code45:Jump Game II

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.

Your goal is to reach the last index in the minimum number of jumps.

Example:

Input: [2,3,1,1,4]
Output: 2
Explanation: The minimum number of jumps to reach the last index is 2.
Jump 1 step from index 0 to 1, then 3 steps to the last index.
Note:
You can assume that you can always reach the last index.

相比第一题可以知道,这个是肯定能到达,然后给出最小的步数。
首先想到回溯法,所有的走一道得到数组,数组取最小值。
百分百超时。

优化思想:
扩大步范围。
每一步最大可以达到哪里(或者说每一步的范围是多大,然后根据范围求得最后点的位置)
我的处理:新建数组,存储对应index的步数。遍历过来的时候做一个对比,确定起点终点,然后层层重叠得到最大的步数范围。理论上是可以,但是操作依然会TLE。
代码:

class Solution2:
    def jump(self,nums):#TLE  add if to pass......
        if len(nums) == 1:
            return 0
        else:
            new_arr = [0] * len(nums)
            for i in range(len(nums)):
                #print(new_arr)
                start = i+1
                end = i+nums[i]
                if end >=len(nums)-1:
                    return new_arr[i]+1
                else:
                    new_arr = self.compare(new_arr,start,end,new_arr[i])
                    #new_arr = new_arr[:start] + (end - start + 1) * [new_arr[i] + 1] + new_arr[end + 1:]

    def compare(self,arr,start,end,last):
        #arr = arr[:start]+(end-start+1)*[last+1]+arr[end+1:]
        for i in range(start,end+1):
            if arr[i] ==0:
                arr[i] = last+1
        return arr

主要是第二步的数组迭代耗时严重(其实事件复杂度问题不大,但是具体问题就不一样了,最差可以到n^2)

然后继续优化:
考虑不需要数组。不要数组依然是可行的,因为存在步范围的上下边界。通过中间变量的变化进行调整。
初始步数:step=0
初始步起点:step_start = 0
初始步终点:step_end = 0

遍历:step_end = max(自身,当前最大范围i+nums[i]。 可逐步扩大终点范围
临界点(找到):如果出现step_end >=len-1时 则表示可以到达最右。返回step+1(因为此时是下一步的范围,找到则加一返回)
临界点(范围溢出):当遍历的i到了上一个初始步的终点(目前就是初始步的起点)变换变量。 step+=1, step_start = step_end。(目的是进入下一步。此时前一步的范围已经遍历完,起点变 现在的终点)

如此下去,必然可以使用尽可能短的时间 找到最小步数。
理念:贪心(尽可能扩大当前的范围)
代码:

class Solution4:#greedy writed by myself
    def jump(self,nums):

        #new_arr = [0] * len(nums)
        step_start = 0
        step_end = 0
        step = 0
        for i in range(len(nums)):
            step_end = max(step_end,i+nums[i])
            if step_end >= len(nums)-1:
                return step+1
            else:
                if i == step_start:
                    step+=1
                    step_start = step_end
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值