Solution: Furthest Building You Can Reach
Tags: algorithms, javascript, java, python
Published: 2021-04-26 09:33:43

*This is part of a series of Leetcode solution explanations ([index](https://dev.to/seanpgallivan/leetcode-solutions-index-57fl)). If you liked this solution or found it useful,* ***please like*** *this post and/or* ***upvote*** *[my solution post on Leetcode's forums](https://leetcode.com/problems/furthest-building-you-can-reach/discuss/1177243).*

---

#### [Leetcode Problem #1642 (*Medium*): Furthest Building You Can Reach](https://leetcode.com/problems/furthest-building-you-can-reach/)

---

#### ***Description:***

<br />(*Jump to*: [*Solution Idea*](#idea) || *Code*: [*JavaScript*](#javascript-code) | [*Python*](#python-code) | [*Java*](#java-code) | [*C++*](#c-code))

>
You are given an integer array heights representing the heights of buildings, some bricks, and some ladders.
>
You start your journey from building 0 and move to the next building by possibly using bricks or ladders.
>
While moving from building i to building i+1 (0-indexed),
  -  If the current building's height is greater than or equal to the next building's height, you do not need a ladder or bricks.
  -  If the current building's height is less than the next building's height, you can either use one ladder or (h[i+1] - h[i]) bricks.
>
Return the furthest building index (0-indexed) you can reach if you use the given ladders and bricks optimally.

---

#### ***Examples:***
>
Example 1:||
|---:|---|
Input:|heights = [4,2,7,6,9,14,12], bricks = 5, ladders = 1
Output:|4
Explanation:|Starting at building 0, you can follow these steps:<br>- Go to building 1 without using ladders nor bricks since 4 >= 2.<br>- Go to building 2 using 5 bricks. You must use either bricks or ladders because 2 < 7.<br>- Go to building 3 without using ladders nor bricks since 7 >= 6.<br>- Go to building 4 using your only ladder. You must use either bricks or ladders because 6 < 9.<br>It is impossible to go beyond building 4 because you do not have any more bricks or ladders.
Visual:|![Example 1 Visual](https://assets.leetcode.com/uploads/2020/10/27/q4.gif)
>
Example 2:||
|---:|---|
Input:|heights = [4,12,2,7,3,18,20,3,19], bricks = 10, ladders = 2
Output:|7
>
Example 3:||
|---:|---|
Input:|heights = [14,3,19,3], bricks = 17, ladders = 0
Output:|3

---

#### ***Constraints:***
>
 - `1 <= heights.length <= 10^5`
 - `1 <= heights[i] <= 10^6`
 - `0 <= bricks <= 10^9`
 - `0 <= ladders <= heights.length`

---

#### ***Idea:***

<br />(*Jump to*: [*Problem Description*](#description) || *Code*: [*JavaScript*](#javascript-code) | [*Python*](#python-code) | [*Java*](#java-code) | [*C++*](#c-code))

The first realization should be that we always want to use our ladders to their best effect: where they would save us the most amount of bricks. Unfortunately, we can't just save the ladders for the largest gaps in the building heights, because we may not be able to reach them by using bricks.

Since we can't find out how far we can go until we figure out where to place the ladders, and we can't figure out where to put the ladders until we see how far we can go, we'll have to move the ladders around as we go in order to maintain their maximum effect.

To put this in terms of a coding solution, as we iterate through the building heights array (**H**), we'll want to continuously make sure that the largest **L** number of positive differences are occupied with ladders, allowing us the best use of our **B** number of bricks.

In general, this means that we should start iterating through **H**, ignoring any difference (**diff**) that is **0** or less, and place a ladder whenever we have a positive difference. Once we've used up all the ladders, then we can start using bricks. If we come across a **diff** that is larger than the smallest ladder that we're currently using, we should replace that ladder with bricks and move the ladder forwad to the current **diff**. Otherwise we should use bricks for the current **diff**.

The second big realization at this point is that we need a **min-heap** or **min priority queue** in order to keep track of the heights of the ladders in use, so that we can always take the smallest one, thus keeping the ladders always on the largest diff values.

If at any point we run out bricks, then we can't reach the next building and should **return i**. If we're able to reach the end of **H** without running out of bricks, we can **return H.length - 1**, signifying that we reached the last building.

 - _**Time Complexity: O(N)** where N is the length of H_
 - _**Space Complexity: O(L)** to keep track of L number of ladder lengths_

---

#### ***Implementation:***

The Javascript **MinPriorityQueue()** npm package isn't as efficient as a **min-heap**, but it is much more succinct, so I've included both versions of code for comparison.

For C++, the **priority_queue** defaults to a max order, so we can just invert the sign on the **diff** values before insertion to effectively turn it into a min priority queue instead.

---

#### ***Javascript Code:***
<br />(*Jump to*: [*Problem Description*](#description) || [*Solution Idea*](#idea))

##### ***w/ MinPriorityQueue():***
```javascript
var furthestBuilding = function(H, B, L) {
    let len = H.length - 1,
        pq = new MinPriorityQueue({priority: x => x})
    for (let i = 0; i < len; i++) {
        let diff = H[i+1] - H[i]
        if (diff > 0) {
            if (L > 0) pq.enqueue(diff), L--
            else if (pq.front() && diff > pq.front().element)
                pq.enqueue(diff), B -= pq.dequeue().element
            else B -= diff
            if (B < 0) return i
        }
    }
    return len
};
```

##### ***w/ Min-Heap:***
```javascript
var furthestBuilding = function(H, B, L) {
    let len = H.length - 1, heap = [,]
    const heapify = val => {
        let i = heap.length, par = i >> 1, temp
        heap.push(val)
        while (heap[par] > heap[i]) {
            temp = heap[par], heap[par] = heap[i], heap[i] = temp
            i = par, par = i >> 1
        }
    }
    const extract = () => {
        if (heap.length === 1) return null
        let top = heap[1], left, right, temp,
            i = 1, child = heap[3] < heap[2] ? 3 : 2
        if (heap.length > 2) heap[1] = heap.pop()
        else heap.pop()
        while (heap[i] > heap[child]) {
            temp = heap[child], heap[child] = heap[i], heap[i] = temp
            i = child, left = i << 1, right = left + 1
            child = heap[right] < heap[left] ? right : left
        }
        return top
    }    
    for (let i = 0; i < len; i++) {
        let diff = H[i+1] - H[i]
        if (diff > 0) {
            if (L > 0) heapify(diff), L--
            else if (heap.length > 1 && diff > heap[1])
                heapify(diff), B -= extract()
            else B -= diff
            if (B < 0) return i
        }
    }
    return len
};
```

---

#### ***Python Code:***
<br />(*Jump to*: [*Problem Description*](#description) || [*Solution Idea*](#idea))
```python
class Solution:
    def furthestBuilding(self, H: List[int], B: int, L: int) -> int:
        heap = []
        for i in range(len(H) - 1):
            diff = H[i+1] - H[i]
            if diff > 0:
                if L > 0:
                    heappush(heap, diff)
                    L -= 1
                elif heap and diff > heap[0]:
                    heappush(heap, diff)
                    B -= heappop(heap)
                else: B -= diff
                if B < 0: return i
        return len(H) - 1
```

---

#### ***Java Code:***
<br />(*Jump to*: [*Problem Description*](#description) || [*Solution Idea*](#idea))
```java
class Solution {
    public int furthestBuilding(int[] H, int B, int L) {
        int len = H.length - 1;
        PriorityQueue<Integer> pq = new PriorityQueue<>();
        for (int i = 0; i < len; i++) {
            int diff = H[i+1] - H[i];
            if (diff > 0) {
                if (L > 0) {
                    pq.add(diff);
                    L--;
                } else if (pq.size() > 0 && diff > pq.peek()) {
                    pq.add(diff);
                    B -= pq.poll();
                } else B -= diff;
                if (B < 0) return i;
            }
        }
        return len;
    }
}
```

---

#### ***C++ Code:***
<br />(*Jump to*: [*Problem Description*](#description) || [*Solution Idea*](#idea))
```c++
class Solution {
public:
    int furthestBuilding(vector<int>& H, int B, int L) {
        int len = H.size() - 1;
        priority_queue<int> pq;
        for (int i = 0; i < len; i++) {
            int diff = H[i+1] - H[i];
            if (diff > 0) {
                if (L) pq.push(-diff), L--;
                else if (!pq.empty() && diff > -pq.top())
                    pq.push(-diff), B += pq.top(), pq.pop();
                else B -= diff;
                if (B < 0) return i;
            }
        }
        return len;
    }
};
```