leetcode403. Frog Jump
題目要求
A frog is crossing a river. The river is divided into x units and at each unit there may or may not exist a stone. The frog can jump on a stone, but it must not jump into the water. Given a list of stones' positions (in units) in sorted ascending order, determine if the frog is able to cross the river by landing on the last stone. Initially, the frog is on the first stone and assume the first jump must be 1 unit. If the frog's last jump was k units, then its next jump must be either k - 1, k, or k + 1 units. Note that the frog can only jump in the forward direction. Note: The number of stones is ≥ 2 and is < 1,100. Each stone's position will be a non-negative integer < 231. The first stone's position is always 0. Example 1: [0,1,3,5,6,8,12,17] There are a total of 8 stones. The first stone at the 0th unit, second stone at the 1st unit, third stone at the 3rd unit, and so on... The last stone at the 17th unit. Return true. The frog can jump to the last stone by jumping 1 unit to the 2nd stone, then 2 units to the 3rd stone, then 2 units to the 4th stone, then 3 units to the 6th stone, 4 units to the 7th stone, and 5 units to the 8th stone. Example 2: [0,1,2,3,4,8,9,11] Return false. There is no way to jump to the last stone as the gap between the 5th and 6th stone is too large.
假設有一隻青蛙需要過河,河中會有一些石子,青蛙必須踩在石頭上才算成功。石頭的位置用整數陣列來表示。青蛙的行走規則為:假設上一次青蛙跳了k格,則當前青蛙只能跳k-1或k或k+1格,且青蛙只能向前跳,不能向後跳。
廣度優先遍歷
可以從起點開始,對從該點出發的所有可能步數進行遍歷,並更新從該點可達的點的所有的可行步數。利用Map<Integer, Set<Integer>>
資料結構來記錄該結果,其中map的key為stone的unit數,它的value對應的是從該key出發的所有的上一步的步長。該遍歷思路類似於廣度優先遍歷,即將該點出發遍歷所有的可達點。程式碼如下:
public boolean canCross(int[] stones) { if(stones.length < 2) return true; if(stones.length == 2 && stones[1] == 1) return true; if(stones.length >= 2 && stones[1] != 1) return false; Map<Integer, Set<Integer>> stoneJump = new HashMap<>(); for(int i = 1 ; i<stones.length ; i++) { stoneJump.put(stones[i], new HashSet<>()); } stoneJump.get(1).add(1); int finalStone = stones[stones.length-1]; boolean hasNext = false; for(int i = 1 ; i<stones.length; i++) { for(int step : stoneJump.get(stones[i])) { int next = stones[i] + step - 1; for(int addOn = -1 ; addOn <= 1 ; addOn++) { if(step + addOn != 0) { if(next == finalStone) { return true; } if(stoneJump.containsKey(next)) { stoneJump.get(next).add(step + addOn); hasNext = true; } } next++; } } if(!hasNext) break; hasNext = false; } return false; }
深度優先遍歷
和上一種思路的區別在於,這種方法會盡可能往遠處遍歷。即只要該點可以到達下一點,則會立刻嘗試從一點到達終點。程式碼如下:
public boolean canCross(int[] stones) { for(int i = 1 ; i<stones.length ; i++) { if(stones[i] - stones[i-1] > i) return false; } return canCross2(stones, 1, 1); } public boolean canCross2(int[] stones, int idx, int lastStep) { if(idx == stones.length-1) return true; if(idx < 0 || lastStep <= 0) return false; for(int jump = lastStep + 1 ; jump >= lastStep -1 ; jump--) { if(canCross2(stones, Arrays.binarySearch(stones, stones[idx] + jump), jump)){ return true; } } return false; }