2

题目要求

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;
    }

raledong
2.7k 声望2k 粉丝

心怀远方,负重前行