1. 程式人生 > >LeetCode Medium 55 跳躍遊戲 Python

LeetCode Medium 55 跳躍遊戲 Python

方法一:

    演算法:貪心

        首先要明確如何向後跳是一個好的選擇,從某個位置i向後跳的時候,可以選擇的位置有nums[i]個(nums[i]儲存

        了第i個位置可以向後跳的步數)。應當選擇這nums[i]個位置中,又可以向後跳到最遠位置的那個位置處:

            解釋:

                譬如當前位置為i=0,可以向後跳3步,即從i=0出發最遠可以到達j=0+3=3的位置,那麼下一步如何跳要

                考察i+1,i+2,i+3這三個位置可以跳到的最遠處,譬如這三個位置最遠可達位置分別為4,6,3,那麼

                下一步應該跳到i+2這個位置,因為這個位置可以到達位置i=6這裡

        且若此時在i處,最遠可達j,則一定可以到達i+1,i+2,...,j-1,j處

 

     解題

            1. 建立"最遠可達陣列",即i+nums[i],求得nums中每個位置最遠可達位置

            2. 設定curr=0起跳位置,以及當前最大可達位置max_reach,curr的意義就是逐個向後走,在最大可達

               位置內挪動,如i=0可達4,那麼curr一定可以向後走到i=1,2,3,4。當curr走到i=1,2,3,4的途中,應該

               判斷當前最大可達位置是否變大了,如果變大了就可以繼續curr += 1一步一步向後走並更新最大可達位置

               (上面提過了,因為如果可以從i到達j,那麼一定可以到i+1,i+2,...,j-1,j處,所以走到i處時,max_reach

               變了,就更新為更大的max_reach,curr++直到<=max_reach)

               所以可以設定curr記錄當前到達位置,並且逐步向後移

            3. 令curr開始挪動,判斷是否能抵達陣列末尾,向下一個位置移動的條件是當前所在位置沒有超過當前

               最大可達位置,並且curr當前位置應該是小於陣列長度的

            4. 跳出迴圈後,curr記錄的就是最後到達的位置,如果到達的位置就是陣列末尾,也就是說在最大可達範圍內

               一步一步走到了最後的陣列末尾,則返回True否則False

        注意

            我自己想的"解法"是,同樣地也算出來reach陣列記錄每個位置能到達的最遠位置,那麼逐個遍歷去判斷陣列

            內的最大可達位置是否有一個reach[x] >= len(nums)-1 的不就好了,即陣列中有一個位置是到達該位置

            後一定可以到達陣列末端的,但是!這樣的想法忽略了【跳躍】的概念,即首先你得能到達那個可達陣列末端

            的節點,如[1,1,0,0,999,999,1],因為中間有0,且前面的1,1不給力,雖然後面有999這種肯定能到達

            末端的位置,但是根本無法從i=0處跳到999,所以要用上面【解題】中的方法,在最大可達位置中curr += 1

        複雜度分析

            時間:ON,建立輔助陣列ON,curr挪動遍歷一遍陣列ON

            空間:ON,記錄最大可達位置的輔助陣列

def canJump(self, nums):

    if nums == []:

        return False

    reach = []

    for i in range(len(nums)):

        reach.append(i + nums[i])

    curr = 0

    max_reach = reach[0]

    while curr < len(nums) and curr <= max_reach:

        if reach[curr] > max_reach:

            max_reach = reach[curr]

        curr += 1

    return curr == len(nums)

方法二:

    演算法:貪心2-逆向思維

            反過來思考整個跳躍的過程,如果能從某一個節點跳躍到達最後一個節點,那麼從最後一個節點回看,一定能

        到達第一個初始節點。

    思路:

        如果有一條路徑A->B->C->D->FINAL,即最後到達FINAL,那麼從FINAL開始向前看,若D是能到達FINAL的,

        那麼前面的位置只要能到達D就是到達FINAL的充分條件了,能到達D則一定能到達FINAL,倒著來看!

 

        所以倒著遍歷陣列,設定一個last位置為最後一個需要被前面位置可達的位置,從最後一個位置開始,last = FINAL,

        判斷它的前一個節點pre的可達位置是否包括last位置,如果包括,即pre+nums[pre] >= last的話,就說明該pre節

        點可達last,更新last = pre,繼續向後判斷,看後面的節點可否達當前last,依次迴圈遍歷,直到最後判斷last == 0

        即最後一個需要可達的位置為初始節點

def canJump_( nums):

    if nums == []:

        return False

    # 初始last是最後一個節點

    last = len(nums) - 1

    # 這裡其實初始化為len(nums)-2,-1,-1 也是可以的,從倒數第二個節點開始判斷是否可達倒數第一個節點,

    #但是從len(nums) - 1, -1, -1雖然冗餘一次但是健壯性更好,譬如len(nums)=1時,1-2 = -1 不好

    #雖然提交了也AC了但是顯然不健壯

    for i in range(len(nums) - 1, -1, -1):

        if i + nums[i] >= last:

            last = i

    return last == 0