日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

贪心算法及Jump Game系列题详解

發布時間:2023/12/29 编程问答 40 豆豆
生活随笔 收集整理的這篇文章主要介紹了 贪心算法及Jump Game系列题详解 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

本博文所有的代碼均可在
https://github.com/Hongze-Wang/LeetCode_Java
https://github.com/Hongze-Wang/LeetCode_Python
中找到,歡迎star。

貪心算法屬于比較難的算法,一般用于求解最優解或者極限情況下判斷可能性。貪心和動態規劃的區別在于,貪心算法的解題過程中會展現出最優子結構,子問題的最優解組合構成了全局的最優解,而動態規劃則是考慮所有子問題,針對這些子結構求解,從中選取出最優解。

貪心算法有兩個重要組成部分:

  • 貪心策略
  • 最優子結構
  • 貪心策略的選取將對貪心算法能否得到最優解起到了決定性的作用。最優子結構指的是,大問題分解成小問題時,使用擬定好的貪心策略一樣能得到小問題的最優解。

    詳見Greedy Analysis Strategies,摘自普林斯頓大學算法課件。
    ?
    貪心算法和動態規劃的共同點在于分治思想,大問題拆解成小問題,從求解小問題過程中得到大問題的解。

    貪心算法和動態規劃的不同點在于最優子結構,即貪心算法相當于認定了貪心策略下的解能得到最優解;而動態規劃則窮舉了所有的解的可能性,從中經過比較得到了最優解。

    因此貪心算法不一定能保證最優解,但可以保證一個還不錯的解。動態規劃一定能保證最優解,但因為它要窮舉所有可能性,計算復雜度會很高,在很多場景下無法使用。

    注:如果貪心策略是解決問題的唯一策略,那么貪心算法也一定能取得最優解,而且比動態規劃復雜度要小得多。

    Jump Game

    來自LeetCode的一組題,我們這里只介紹貪心算法,其中55題既可以使用貪心解也可以使用動態規劃,它是一個貪心算法復雜度低于動態規劃的一個實例。官方注釋是英文的,因此我加的注釋也是英文的,如果有看不懂的地方可以在評論區提問。

  • Jump Game (Medium)
  • Jump Game II (Hard)
  • Jump Game III (Medium)
  • 55. Jump Game (Medium)

    Given an array of non-negative integers nums, 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: nums = [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: 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.

    Constraints
    1 <= nums.length <= 3 * 104
    0 <= nums[i] <= 105

    貪心策略:選取最大的步數,從結束點逆推到起始點,看能不能回到起始點,如果能,自然也能從起始點到達結束點。選取最大步數在程序中體現的是i+nums[i],如果i+nums[i]>=lastPos,那么從i是能到達lastPos的。

    // 55. Jump Game// The Solution of this question is very detail and inspring // I recommend you to read it for more info and idea: https://leetcode.com/problems/jump-game/solution/// Usually, solving and fully understanding a dynamic programming problem is a 4 step process: // Start with the recursive backtracking solution // Optimize by using a memoization table (top-down dynamic programming) // Remove the need for recursion (bottom-up dynamic programming) // Apply final tricks to reduce the time / memory complexity// Approach 1: Backtracking (Brute Force) 這里沒有回溯過程 所以我覺得不能稱作回溯法 只是單純的DFS而已 // check every pos from curr pos and all the reachable // Time Limit Exceeded class Solution {public boolean canJumpFromPosition(int pos, int[] nums) {if(pos == nums.length - 1) {return true;}int fur = Math.min(pos + nums[pos], nums.length-1);for(int next = pos+1; next<=fur; next++) {if(canJumpFromPosition(next, nums)) {return true;}}return false;}public boolean canJump(int[] nums) {return canJumpFromPosition(0, nums);} }// Approach 1: Backtracking (Brute Force) Optimized // check every pos from curr pos and all the reachable from right to left // this means we always try to make the biggest jump such that we reach the end as soon as possible (greedy policy) // Time Limit Exceeded class Solution {public boolean canJumpFromPosition(int pos, int[] nums) {if(pos == nums.length - 1) {return true;}int fur = Math.min(pos + nums[pos], nums.length-1);for(int next = fur; next>pos; next--) {if(canJumpFromPosition(next, nums)) {return true;}}return false;}public boolean canJump(int[] nums) {return canJumpFromPosition(0, nums);} }// Approach 2: Dynamic Programming Top-down // It relies on the observation that once we determine that a certain index is good / bad, this result will never change. // This means that we can store the result and not need to recompute it every time. // memoizationenum Index {GOOD, BAD, UNKNOW }public class Solution {Index[] memo;public boolean canJumpFromPosition(int pos, int[] nums) {if(memo[pos] != Index.UNKNOW) {return memo[pos] == Index.GOOD ? true : false;}int fur = Math.min(pos+nums[pos], nums.length-1);for(int next=pos+1; next<=fur; next++) {if(canJumpFromPosition(next, nums)) {memo[pos] = Index.GOOD;return true; }}memo[pos] = Index.BAD;return false;}public boolean canJump(int[ ] nums) {memo = new Index[nums.length];for(int i=0; i<memo.length; i++) {memo[i] = Index.UNKNOW;}memo[memo.length-1] = Index.GOOD;return canJumpFromPosition(0, nums);} }// Top down dp public class Solution {public boolean canJumpFromPostion(int pos, int[] nums, int[] memo) {if(memo[pos] != 0) {return memo[pos] == 1 ? true : false;}int fur = Math.min(pos+nums[pos], nums.length-1);for(int next=pos+1; next <= fur; next++) {if(canJumpFromPostion(next, nums, memo)) {memo[pos] = 1;return true;}}memo[pos] = 2;return false;}public boolean canJump(int[] nums) {int[] memo = new int[nums.length];memo[nums.length-1] = 1;return canJumpFromPostion(0, nums, memo);} }// Approach 3: Dynamic Programming Bottom-up // Top-down to bottom-up conversion is done by eliminating recursion // The recursion is usually eliminated by trying to reverse the order of the steps from the top-down approach.enum Index {GOOD, BAD, UNKNOW }public class Solution {public boolean canJump(int[] nums) {Index[] memo = new Index[nums.length];for(int i=0; i<nums.length; i++) {memo[i] = Index.UNKNOW;}memo[nums.length-1] = Index.GOOD;for(int i=nums.length-2; i>=0; i--) {int fur = Math.min(i+nums[i], nums.length-1);for(int j=i+1; j<=fur; j++) {if(memo[j] == Index.GOOD) {memo[i] = Index.GOOD;break;} }}return memo[0] == Index.GOOD;} }// Bottom up dp public class Solution {public boolean canJump(int[] nums) {int[] memo = new int[nums.length];memo[nums.length-1] = 1;for(int i=nums.length-2; i>=0; i--) {int fur = Math.min(i+nums[i], nums.length-1);for(int j=i+1; j<=fur; j++) {if(memo[j] == 1) {memo[i] = 1;break;}}}return memo[0] == 1;} }// Approach 4: Greedy // Greedy Policy // try to make the biggest jump such that we reach the start from the end as soon as possible public class Solution {public boolean canJump(int[] nums) {int lastPos = nums.length-1;for(int i=nums.length-1; i>=0; i--) {if(i+nums[i] >= lastPos) {lastPos = i;}}return lastPos == 0;} }

    動態規劃再怎么優化都是O(n^2),但貪心是O(n)復雜度確實要小得多。

    45. Jump Game II (Hard)

    Given an array of non-negative integers nums, 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.
    You can assume that you can always reach the last index.

    Example 1:
    Input: nums = [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.

    Example 2:
    Input: nums = [2,3,0,1,4]
    Output: 2

    Constraints:
    1 <= nums.length <= 3 * 104
    0 <= nums[i] <= 105

    這道題比上一道考察貪心的傾向更明顯,因為它求的是最小步數,是個求最優解問題。貪心策略同上,也是每次都選取能走的最大步數,最后一次走的時候,如果i+nums[i] > nums.length-1而溢出,那么i == pos不會成立,因而不會再計次數。

    // 45. Jump Game II // greedy: take the highest step as you can in every jumpclass Solution {public int jump(int[] nums) {if(nums.length == 1) return 0;int pos = 0, reachable = 0, count = 0;for(int i=0; i<nums.length-1; i++) { // last elem do not need considerreachable = Math.max(reachable, i+nums[i]);if(i == pos) {pos = reachable;count++;}}return count;} }// reachable store the current highest position it can reach // pos store the last hight postion it can reach // if i == pos means that i can be reach // update pos = reachable (currennt highest postion it can rech) greedy policy // count increase by one

    1306. Jump Game III (Medium)

    Given an array of non-negative integers arr, you are initially positioned at start index of the array. When you are at index i, you can jump to i + arr[i] or i - arr[i], check if you can reach to any index with value 0.

    Notice that you can not jump outside of the array at any time.

    Example 1:
    Input: arr = [4,2,3,0,3,1,2], start = 5
    Output: true
    Explanation:
    All possible ways to reach at index 3 with value 0 are:
    index 5 -> index 4 -> index 1 -> index 3
    index 5 -> index 6 -> index 4 -> index 1 -> index 3

    Example 2:
    Input: arr = [4,2,3,0,3,1,2], start = 0
    Output: true
    Explanation:
    One possible way to reach at index 3 with value 0 is:
    index 0 -> index 4 -> index 1 -> index 3

    Example 3:
    Input: arr = [3,0,2,1,2], start = 2
    Output: false
    Explanation: There is no way to reach at index 1 with value 0.

    Constraints:
    1 <= arr.length <= 5 * 104
    0 <= arr[i] < arr.length
    0 <= start < arr.length

    這道題雖然也放到這里了,但不是貪心,因為每次跳法只有兩種,這兩種跳法無法判斷誰最優,而且也不是求最優解問題。DFS或者BFS都可以做,下面的解法使用了一個trick,使用取負來標志一個元素被訪問過。

    // 1306. Jump Game III// DFS class Solution {public boolean canReach(int[] arr, int start) {if(start < 0 || start >= arr.length || arr[start] < 0) return false;if(arr[start] == 0) return true;arr[start] = -arr[start]; // mark arr[start] as visitedreturn canReach(arr, start+arr[start]) || canReach(arr, start-arr[start]);} }// BFS class Solution {public boolean canReach(int[] arr, int start) {int n=arr.length;Queue<Integer> q = new LinkedList();q.add(start);while(!q.isEmpty()) {int node = q.poll();if(arr[node] == 0) return true;if(arr[node] < 0) continue;if(node + arr[node] < n) {q.offer(node + arr[node]);}if(node - arr[node] >=0) {q.offer(node - arr[node]);}arr[node] = -arr[node];}return false;} }

    總結

    以上是生活随笔為你收集整理的贪心算法及Jump Game系列题详解的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。