Tuesday, June 21, 2022

1642. Furthest Building You Can Reach

 一刷 06/2022

Version #1 Min Heap - conditionally adding new elements

Time O(NlogL) ~ O(NlogN) in the worst case -> N is the length of buildings, L is number of ladders

Space O(L) ~ O(N) in the worst case

Runtime: 29 ms, faster than 54.02% of Java online submissions for Furthest Building You Can Reach. 
Memory Usage: 79.8 MB, less than 21.66% of Java online submissions for Furthest Building You Can Reach.

class Solution {

    public int furthestBuilding(int[] heights, int bricks, int ladders) {

        // Ensure that ladders are used on the longest climbs

        // If we're out of ladders, we'll replace the most wasteful ladder allocation with bricks

        // min heap is used to store height diff of ladders

        Queue<Integer> minHeap = new PriorityQueue<>();

        int index = 0;

        for (index = 1; index < heights.length; index++) {

            int diff = heights[index] - heights[index - 1];

            if (diff <= 0) {

                continue;

            }

            if (ladders > 0) {

                minHeap.offer(diff);

                ladders--;

                continue;

            }

            // ladders == 0, we want to use bricks to replace ladders

            int min = diff;

            if (!minHeap.isEmpty() && minHeap.peek() < min) {

                min = minHeap.poll();

                minHeap.offer(diff);

            }

            // System.out.printf("index=%d, min=%d, bricks=%d\n", index, min, bricks);

            if (bricks < min) {

                return index - 1;

            }

            bricks -= min;

        }

        return heights.length - 1;

    }

}


Version #2 Max Heap - shorter version

Time O(NlogN) - N number of buildings

Space O(N)

Runtime: 21 ms, faster than 88.96% of Java online submissions for Furthest Building You Can Reach.
Memory Usage: 56.6 MB, less than 83.56% of Java online submissions for Furthest Building You Can Reach.

class Solution {

    public int furthestBuilding(int[] heights, int bricks, int ladders) {

        // Use bricks for the shortest climbs

        Queue<Integer> maxHeap = new PriorityQueue<>(Collections.reverseOrder());

        int index = 0;

        for (index = 1; index < heights.length; index++) {

            int diff = heights[index] - heights[index - 1];

            if (diff <= 0) {

                continue;

            }

            maxHeap.offer(diff);

            bricks -= diff;

            if (bricks >= 0) {

                continue;

            }

            bricks += maxHeap.poll();

            if (ladders > 0) {

                ladders--;

            } else {

                return index - 1;

            }

        }

        return heights.length - 1;

    }

}


Version #3 Binary Search

Binary Search的思路其实是比较好想的,难点在于怎么不重复地sort [0, mid]之间的height difference

如果不优化的话,每一次canReach都需要NlogN的时间,总体就是O(N(logN)^2)的复杂度

这个方法的巧思在于把整个的height difference和它们的index一起sort,如果index不在范围就跳过

Time O(NlogN)

Space O(N)

Runtime: 215 ms, faster than 5.05% of Java online submissions for Furthest Building You Can Reach.
Memory Usage: 80.3 MB, less than 9.25% of Java online submissions for Furthest Building You Can Reach.

class Solution {

    public int furthestBuilding(int[] heights, int bricks, int ladders) {

        // Globally sort the difference of heights

        List<List<Integer>> diffs = new ArrayList<>();

        for (int i = 1; i < heights.length; i++) {

            int diff = heights[i] - heights[i - 1];

            if (diff > 0) {

                diffs.add(Arrays.asList(new Integer[]{diff, i}));

            }

        }

        Collections.sort(diffs, (a, b) -> a.get(0) - b.get(0));

        int start = 0, end = heights.length - 1;

        // Find the last index that can reach

        while (start + 1 < end) {

            int mid = start + (end - start) / 2;

            if (canReach(mid, diffs, bricks, ladders)) {

                start = mid;

            } else {

                end = mid - 1;

            }

        }

        if (canReach(end, diffs, bricks, ladders)) {

            return end;

        }

        return start;

    }

    

    private boolean canReach(int index, List<List<Integer>> diffs, int bricks, int ladders) {

        for (List<Integer> diff : diffs) {

            if (diff.get(1) > index) {

                continue;

            }

            if (bricks >= diff.get(0)) {

                bricks -= diff.get(0);

            } else {

                ladders--;

            }

        }

        return ladders >= 0;

    }

}

No comments:

Post a Comment