Facebook Pixel

2662. Minimum Cost of a Path With Special Roads

Problem Description

You are given a starting position and a target position on a 2D plane, and you need to find the minimum cost to travel from start to target.

The problem provides:

  • start = [startX, startY]: your initial position coordinates
  • target = [targetX, targetY]: your destination coordinates
  • specialRoads: an array of special one-way roads

The normal cost of moving from any position (x1, y1) to another position (x2, y2) is the Manhattan distance: |x2 - x1| + |y2 - y1|.

However, there are special roads that can potentially reduce travel cost. Each special road is defined as [x1, y1, x2, y2, cost], which means:

  • The road goes from position (x1, y1) to position (x2, y2)
  • The road is one-directional (can only be traveled from (x1, y1) to (x2, y2))
  • Using this road costs exactly cost units (which may be less than the Manhattan distance)
  • You can use any special road multiple times

Your task is to find the minimum total cost to reach the target from the start position. You can:

  1. Move directly using Manhattan distance between any two points
  2. Use special roads when beneficial
  3. Combine regular movement and special roads in any way

For example, you might walk normally to the start of a special road, use the special road, then walk normally from its end to your target - if that results in a lower total cost than walking directly.

Quick Interview Experience
Help others by sharing your interview experience
Have you seen this problem before?

Intuition

The key insight is that this is a shortest path problem in a graph where we need to consider both normal movement and special roads.

Think about it this way: at any position (x, y) we're currently at, we have multiple choices:

  1. Walk directly to the target (cost: Manhattan distance)
  2. Walk to the start of any special road, use that road, and continue from there

Since special roads might offer shortcuts, we can't just greedily walk straight to the target. We need to explore different paths and find the one with minimum cost.

The critical observation is that special roads have fixed endpoints. This means the only "interesting" positions we need to consider are:

  • Our starting position
  • The endpoints of special roads (both start and end points)
  • Our target position

From any position, we can either:

  • Go directly to the target
  • Go to the start of a special road (paying Manhattan distance), then use that road

This naturally leads to a graph search problem where we want to find the minimum cost path. Since we're dealing with weighted edges (different costs for different paths), Dijkstra's algorithm is perfect for this.

The algorithm explores positions in order of their distance from the start. For each position (x, y) we visit with cost d:

  • We check if going directly to the target from here is better: d + dist(x, y, target)
  • We consider taking each special road by first walking to its start: d + dist(x, y, road_start) + road_cost

By processing positions in order of increasing cost (using a priority queue), we ensure we always find the optimal path to each position first. Eventually, we'll have considered all possible combinations of walking and using special roads, giving us the minimum cost to reach the target.

Learn more about Graph, Shortest Path and Heap (Priority Queue) patterns.

Solution Approach

We implement Dijkstra's algorithm to find the shortest path from start to target, considering both normal movement and special roads.

Data Structures Used:

  • Priority Queue (heappop/heappush): Stores positions to visit, ordered by their minimum cost from the start
  • Visited Set (vis): Tracks positions we've already processed to avoid redundant calculations

Implementation Steps:

  1. Initialize the priority queue with the starting position: q = [(0, start[0], start[1])]

    • The tuple format is (cost, x, y) where cost is 0 initially
  2. Define a helper function dist(x1, y1, x2, y2) to calculate Manhattan distance between two points

  3. Process positions using Dijkstra's algorithm:

    while q:
        d, x, y = heappop(q)  # Get position with minimum cost
        if (x, y) in vis:      # Skip if already processed
            continue
        vis.add((x, y))        # Mark as visited
  4. For each position (x, y) with cost d:

    • Update the answer by considering direct movement to target:
      ans = min(ans, d + dist(x, y, *target))
    • Consider all special roads: For each special road from (x1, y1) to (x2, y2) with cost cost:
      heappush(q, (d + dist(x, y, x1, y1) + cost, x2, y2))
      This adds the endpoint (x2, y2) to our queue with total cost:
      • d: cost to reach current position
      • dist(x, y, x1, y1): cost to walk to the special road's start
      • cost: cost to use the special road

Why this works:

  • Dijkstra's algorithm guarantees we process positions in order of increasing distance from the start
  • When we first visit a position, we've found the minimum cost to reach it
  • By considering all special roads from each position, we explore all possible path combinations
  • The algorithm naturally handles using multiple special roads in sequence
  • The final answer is the minimum of all possible ways to reach the target

Time Complexity: O(n² log n) where n is the number of special roads, since we might visit each road endpoint and for each, we consider all special roads.

Space Complexity: O(n) for the priority queue and visited set.

Ready to land your dream job?

Unlock your dream job with a 5-minute evaluator for a personalized learning plan!

Start Evaluator

Example Walkthrough

Let's walk through a concrete example to illustrate the solution approach.

Given:

  • start = [1, 1]
  • target = [4, 5]
  • specialRoads = [[1, 2, 3, 3, 2], [3, 4, 4, 5, 1]]
    • Road 1: from (1,2) to (3,3) with cost 2
    • Road 2: from (3,4) to (4,5) with cost 1

Step-by-step execution:

Initial Setup:

  • Priority queue: [(0, 1, 1)] - start at (1,1) with cost 0
  • Visited set: empty
  • Answer: infinity

Iteration 1: Pop (0, 1, 1)

  • Mark (1,1) as visited
  • Direct to target: cost = 0 + |4-1| + |5-1| = 0 + 3 + 4 = 7
  • Update answer to 7
  • Consider special roads:
    • Road 1: Walk to (1,2) costs |1-1| + |2-1| = 1, then use road (cost 2) to reach (3,3)
      • Add to queue: (0 + 1 + 2, 3, 3) = (3, 3, 3)
    • Road 2: Walk to (3,4) costs |3-1| + |4-1| = 5, then use road (cost 1) to reach (4,5)
      • Add to queue: (0 + 5 + 1, 4, 5) = (6, 4, 5)
  • Queue now: [(3, 3, 3), (6, 4, 5)]

Iteration 2: Pop (3, 3, 3)

  • Mark (3,3) as visited
  • Direct to target: cost = 3 + |4-3| + |5-3| = 3 + 1 + 2 = 6
  • Update answer to 6 (better than 7)
  • Consider special roads:
    • Road 1: Walk to (1,2) costs |1-3| + |2-3| = 3, total would be 3 + 3 + 2 = 8 to reach (3,3) - but (3,3) is already visited, so this gets skipped
    • Road 2: Walk to (3,4) costs |3-3| + |4-3| = 1, then use road (cost 1) to reach (4,5)
      • Add to queue: (3 + 1 + 1, 4, 5) = (5, 4, 5)
  • Queue now: [(5, 4, 5), (6, 4, 5)]

Iteration 3: Pop (5, 4, 5)

  • Mark (4,5) as visited
  • Direct to target: cost = 5 + |4-4| + |5-5| = 5 + 0 = 5
  • Update answer to 5 (better than 6)
  • Consider special roads (both start positions would require walking backwards, making them non-optimal)

Iteration 4: Pop (6, 4, 5)

  • (4,5) already visited, skip

Result: Minimum cost is 5

Path taken: Start (1,1) → Walk to (1,2) with cost 1 → Use Road 1 to (3,3) with cost 2 → Walk to (3,4) with cost 1 → Use Road 2 to (4,5) with cost 1 → Total: 1 + 2 + 1 + 1 = 5

This beats the direct Manhattan distance of 7 by cleverly using both special roads!

Solution Implementation

1from heapq import heappush, heappop
2from typing import List
3from math import inf
4
5class Solution:
6    def minimumCost(
7        self, start: List[int], target: List[int], specialRoads: List[List[int]]
8    ) -> int:
9        """
10        Find the minimum cost to travel from start to target using special roads.
11        Uses Dijkstra's algorithm to explore paths with special roads.
12      
13        Args:
14            start: Starting coordinates [x, y]
15            target: Target coordinates [x, y]
16            specialRoads: List of special roads [x1, y1, x2, y2, cost]
17      
18        Returns:
19            Minimum cost to reach target from start
20        """
21      
22        def calculate_manhattan_distance(x1: int, y1: int, x2: int, y2: int) -> int:
23            """Calculate Manhattan distance between two points."""
24            return abs(x1 - x2) + abs(y1 - y2)
25      
26        # Priority queue: (cost, x_coordinate, y_coordinate)
27        priority_queue = [(0, start[0], start[1])]
28      
29        # Set to track visited positions to avoid reprocessing
30        visited_positions = set()
31      
32        # Initialize minimum cost to infinity
33        min_cost = inf
34      
35        # Dijkstra's algorithm
36        while priority_queue:
37            # Pop position with minimum cost so far
38            current_cost, current_x, current_y = heappop(priority_queue)
39          
40            # Skip if this position was already visited
41            if (current_x, current_y) in visited_positions:
42                continue
43          
44            # Mark current position as visited
45            visited_positions.add((current_x, current_y))
46          
47            # Update minimum cost if we can reach target directly from current position
48            direct_to_target_cost = current_cost + calculate_manhattan_distance(
49                current_x, current_y, target[0], target[1]
50            )
51            min_cost = min(min_cost, direct_to_target_cost)
52          
53            # Explore all special roads from current position
54            for road_start_x, road_start_y, road_end_x, road_end_y, road_cost in specialRoads:
55                # Calculate total cost to reach road end via this special road
56                cost_to_road_start = calculate_manhattan_distance(
57                    current_x, current_y, road_start_x, road_start_y
58                )
59                total_cost_to_road_end = current_cost + cost_to_road_start + road_cost
60              
61                # Add road end position to priority queue for exploration
62                heappush(priority_queue, (total_cost_to_road_end, road_end_x, road_end_y))
63      
64        return min_cost
65
1class Solution {
2    public int minimumCost(int[] start, int[] target, int[][] specialRoads) {
3        // Initialize the minimum cost to a large value
4        int minCost = 1 << 30;
5      
6        // Large constant used for encoding coordinates into a single long value
7        int COORDINATE_MULTIPLIER = 1000000;
8      
9        // Priority queue to store states: [cost, x, y], ordered by cost
10        PriorityQueue<int[]> priorityQueue = new PriorityQueue<>((a, b) -> a[0] - b[0]);
11      
12        // Set to track visited positions (encoded as long values)
13        Set<Long> visited = new HashSet<>();
14      
15        // Start from the initial position with cost 0
16        priorityQueue.offer(new int[] {0, start[0], start[1]});
17      
18        // Dijkstra's algorithm to find the minimum cost path
19        while (!priorityQueue.isEmpty()) {
20            int[] current = priorityQueue.poll();
21            int currentCost = current[0];
22            int currentX = current[1];
23            int currentY = current[2];
24          
25            // Encode current position as a single long value for visited check
26            long encodedPosition = 1L * currentX * COORDINATE_MULTIPLIER + currentY;
27          
28            // Skip if this position has already been visited
29            if (visited.contains(encodedPosition)) {
30                continue;
31            }
32          
33            // Mark current position as visited
34            visited.add(encodedPosition);
35          
36            // Update minimum cost if we can reach target from current position
37            int costToTarget = currentCost + dist(currentX, currentY, target[0], target[1]);
38            minCost = Math.min(minCost, costToTarget);
39          
40            // Explore all special roads from current position
41            for (int[] road : specialRoads) {
42                int roadStartX = road[0];
43                int roadStartY = road[1];
44                int roadEndX = road[2];
45                int roadEndY = road[3];
46                int roadCost = road[4];
47              
48                // Calculate total cost to reach the end of this special road
49                int costToRoadStart = dist(currentX, currentY, roadStartX, roadStartY);
50                int totalCost = currentCost + costToRoadStart + roadCost;
51              
52                // Add new state to priority queue
53                priorityQueue.offer(new int[] {totalCost, roadEndX, roadEndY});
54            }
55        }
56      
57        return minCost;
58    }
59  
60    /**
61     * Calculate Manhattan distance between two points
62     * @param x1 x-coordinate of first point
63     * @param y1 y-coordinate of first point
64     * @param x2 x-coordinate of second point
65     * @param y2 y-coordinate of second point
66     * @return Manhattan distance between the two points
67     */
68    private int dist(int x1, int y1, int x2, int y2) {
69        return Math.abs(x1 - x2) + Math.abs(y1 - y2);
70    }
71}
72
1class Solution {
2public:
3    int minimumCost(vector<int>& start, vector<int>& target, vector<vector<int>>& specialRoads) {
4        // Lambda function to calculate Manhattan distance between two points
5        auto calculateManhattanDistance = [](int x1, int y1, int x2, int y2) {
6            return abs(x1 - x2) + abs(y1 - y2);
7        };
8      
9        // Initialize the minimum cost to a large value
10        int minCost = 1 << 30;  // Equivalent to 2^30
11      
12        // Constant for encoding coordinates into a single value
13        const int ENCODING_FACTOR = 1e6;
14      
15        // Priority queue for Dijkstra's algorithm
16        // Stores tuples of (distance, x-coordinate, y-coordinate)
17        // Using greater<> for min-heap behavior
18        priority_queue<tuple<int, int, int>, 
19                      vector<tuple<int, int, int>>, 
20                      greater<tuple<int, int, int>>> pq;
21      
22        // Start from the initial position with cost 0
23        pq.push({0, start[0], start[1]});
24      
25        // Set to track visited positions to avoid revisiting
26        unordered_set<long long> visitedPositions;
27      
28        // Dijkstra's algorithm main loop
29        while (!pq.empty()) {
30            // Extract the position with minimum distance
31            auto [currentDistance, currentX, currentY] = pq.top();
32            pq.pop();
33          
34            // Encode current position into a single value for visited check
35            long long encodedPosition = 1LL * currentX * ENCODING_FACTOR + currentY;
36          
37            // Skip if this position has already been visited
38            if (visitedPositions.count(encodedPosition)) {
39                continue;
40            }
41          
42            // Mark current position as visited
43            visitedPositions.insert(encodedPosition);
44          
45            // Update minimum cost by considering direct path from current position to target
46            minCost = min(minCost, 
47                         currentDistance + calculateManhattanDistance(currentX, currentY, 
48                                                                      target[0], target[1]));
49          
50            // Explore all special roads from current position
51            for (auto& road : specialRoads) {
52                int roadStartX = road[0];
53                int roadStartY = road[1];
54                int roadEndX = road[2];
55                int roadEndY = road[3];
56                int roadCost = road[4];
57              
58                // Calculate total cost: current distance + distance to road start + road cost
59                int totalCost = currentDistance + 
60                               calculateManhattanDistance(currentX, currentY, roadStartX, roadStartY) + 
61                               roadCost;
62              
63                // Add the road's end position to the priority queue
64                pq.push({totalCost, roadEndX, roadEndY});
65            }
66        }
67      
68        return minCost;
69    }
70};
71
1// Calculate Manhattan distance between two points
2function calculateManhattanDistance(x1: number, y1: number, x2: number, y2: number): number {
3    return Math.abs(x1 - x2) + Math.abs(y1 - y2);
4}
5
6// Compare function for priority queue (min-heap based on cost)
7function compareByCost(a: [number, number, number], b: [number, number, number]): number {
8    return a[0] - b[0];
9}
10
11// Main function to find minimum cost path from start to target using special roads
12function minimumCost(start: number[], target: number[], specialRoads: number[][]): number {
13    // Priority queue to store [cost, x, y] tuples, ordered by cost
14    const priorityQueue = new Heap<[number, number, number]>(compareByCost);
15  
16    // Start from the initial position with cost 0
17    priorityQueue.push([0, start[0], start[1]]);
18  
19    // Constant for encoding coordinates into a single number
20    const COORDINATE_MULTIPLIER = 1000000;
21  
22    // Set to track visited positions to avoid revisiting
23    const visitedPositions: Set<number> = new Set();
24  
25    // Initialize answer with a large value
26    let minimumTotalCost = 1 << 30;
27  
28    // Dijkstra's algorithm to find shortest path
29    while (priorityQueue.size()) {
30        // Get the position with minimum cost so far
31        const [currentCost, currentX, currentY] = priorityQueue.pop();
32      
33        // Encode current position as a single number for efficient storage
34        const encodedPosition = currentX * COORDINATE_MULTIPLIER + currentY;
35      
36        // Skip if this position has already been visited
37        if (visitedPositions.has(encodedPosition)) {
38            continue;
39        }
40      
41        // Mark current position as visited
42        visitedPositions.add(encodedPosition);
43      
44        // Update minimum cost if direct path from current position to target is better
45        minimumTotalCost = Math.min(
46            minimumTotalCost, 
47            currentCost + calculateManhattanDistance(currentX, currentY, target[0], target[1])
48        );
49      
50        // Explore all special roads from current position
51        for (const [roadStartX, roadStartY, roadEndX, roadEndY, roadCost] of specialRoads) {
52            // Calculate total cost: current cost + distance to road start + road cost
53            const totalCostToRoadEnd = currentCost + 
54                calculateManhattanDistance(currentX, currentY, roadStartX, roadStartY) + 
55                roadCost;
56          
57            // Add road end position to priority queue for exploration
58            priorityQueue.push([totalCostToRoadEnd, roadEndX, roadEndY]);
59        }
60    }
61  
62    return minimumTotalCost;
63}
64
65// Type definition for comparison function
66type Compare<T> = (lhs: T, rhs: T) => number;
67
68// Min-heap implementation for priority queue
69class Heap<T = number> {
70    // Internal array storage with null at index 0 for easier indexing
71    data: Array<T | null>;
72    // Comparison function for heap ordering
73    lt: (i: number, j: number) => boolean;
74  
75    // Constructor overloads
76    constructor();
77    constructor(data: T[]);
78    constructor(compare: Compare<T>);
79    constructor(data: T[], compare: Compare<T>);
80    constructor(
81        data: T[] | Compare<T> = [],
82        compare: Compare<T> = (lhs: T, rhs: T) => (lhs < rhs ? -1 : lhs > rhs ? 1 : 0),
83    ) {
84        // Handle case where first parameter is comparison function
85        if (typeof data === 'function') {
86            compare = data;
87            data = [];
88        }
89      
90        // Initialize data array with null at index 0 for 1-based indexing
91        this.data = [null, ...data];
92      
93        // Define less-than comparison using provided compare function
94        this.lt = (i, j) => compare(this.data[i]!, this.data[j]!) < 0;
95      
96        // Heapify all elements to maintain heap property
97        for (let i = this.size(); i > 0; i--) {
98            this.heapify(i);
99        }
100    }
101
102    // Return number of elements in heap
103    size(): number {
104        return this.data.length - 1;
105    }
106
107    // Add new element and maintain heap property
108    push(v: T): void {
109        this.data.push(v);
110        let currentIndex = this.size();
111      
112        // Bubble up: swap with parent while current is smaller
113        while (currentIndex >> 1 !== 0 && this.lt(currentIndex, currentIndex >> 1)) {
114            this.swap(currentIndex, currentIndex >> 1);
115            currentIndex >>= 1;
116        }
117    }
118
119    // Remove and return minimum element
120    pop(): T {
121        // Move last element to root
122        this.swap(1, this.size());
123        const minElement = this.data.pop();
124      
125        // Restore heap property from root
126        this.heapify(1);
127        return minElement!;
128    }
129
130    // Return minimum element without removing
131    top(): T {
132        return this.data[1]!;
133    }
134  
135    // Restore heap property starting from index i
136    heapify(i: number): void {
137        while (true) {
138            let minIndex = i;
139            const leftChild = i * 2;
140            const rightChild = i * 2 + 1;
141            const heapSize = this.data.length;
142          
143            // Find minimum among current node and its children
144            if (leftChild < heapSize && this.lt(leftChild, minIndex)) {
145                minIndex = leftChild;
146            }
147            if (rightChild < heapSize && this.lt(rightChild, minIndex)) {
148                minIndex = rightChild;
149            }
150          
151            // If current is not minimum, swap and continue
152            if (minIndex !== i) {
153                this.swap(i, minIndex);
154                i = minIndex;
155            } else {
156                break;
157            }
158        }
159    }
160
161    // Remove all elements from heap
162    clear(): void {
163        this.data = [null];
164    }
165
166    // Swap elements at indices i and j
167    private swap(i: number, j: number): void {
168        const tempData = this.data;
169        [tempData[i], tempData[j]] = [tempData[j], tempData[i]];
170    }
171}
172

Time and Space Complexity

Time Complexity: O(n^2 × log n), where n is the number of special roads.

The algorithm uses Dijkstra's algorithm with a priority queue (min-heap). Here's the breakdown:

  • Each special road endpoint can be visited at most once due to the visited set vis
  • There are at most n special road endpoints plus the starting point, giving us O(n) possible positions
  • For each position we visit, we examine all n special roads to potentially add their endpoints to the queue
  • Each heap operation (push/pop) takes O(log m) time, where m is the size of the heap
  • In the worst case, the heap can contain O(n^2) elements (each of the n positions could potentially be reached via each of the n special roads)
  • Total operations: O(n) positions × O(n) special roads per position × O(log n^2) per heap operation = O(n^2 × 2log n) = O(n^2 × log n)

Space Complexity: O(n^2)

The space usage comes from:

  • The priority queue q can contain at most O(n^2) elements in the worst case (each endpoint of each special road could be added multiple times before being visited)
  • The visited set vis stores at most O(n) positions (one for each special road endpoint plus the start)
  • The overall space complexity is dominated by the priority queue: O(n^2)

Learn more about how to find time and space complexity quickly.

Common Pitfalls

1. Not Considering Direct Path to Target

Pitfall: A common mistake is only updating the minimum cost when reaching the exact target position through the priority queue, rather than considering the direct path from every visited position to the target.

Why it happens: Developers might think they need to reach the target position exactly through the graph traversal, forgetting that you can walk directly from any position to the target using Manhattan distance.

Incorrect approach:

while priority_queue:
    current_cost, current_x, current_y = heappop(priority_queue)
  
    # Wrong: Only check if we've reached the target
    if current_x == target[0] and current_y == target[1]:
        return current_cost
  
    # Process special roads...

Correct approach: Always calculate the cost to reach the target directly from each visited position:

while priority_queue:
    current_cost, current_x, current_y = heappop(priority_queue)
  
    # Correct: Consider direct path to target from current position
    min_cost = min(min_cost, current_cost + calculate_manhattan_distance(
        current_x, current_y, target[0], target[1]
    ))

2. Using Special Roads Only When Cost is Less Than Manhattan Distance

Pitfall: Filtering out special roads where the road cost is greater than or equal to the Manhattan distance between the road's endpoints.

Why it happens: It seems logical that a special road with cost ≥ Manhattan distance wouldn't be useful, but this ignores that the road might position you better for subsequent moves.

Incorrect approach:

for road_start_x, road_start_y, road_end_x, road_end_y, road_cost in specialRoads:
    # Wrong: Filtering "inefficient" roads
    manhattan = calculate_manhattan_distance(road_start_x, road_start_y, road_end_x, road_end_y)
    if road_cost >= manhattan:
        continue  # Skip this road - WRONG!
  
    # Process road...

Why this is wrong: Even if a special road costs more than the direct Manhattan distance between its endpoints, it might still be part of the optimal path because:

  • It could position you closer to other beneficial special roads
  • The detour might align better with the overall path to the target
  • Multiple "inefficient" roads combined might create an efficient route

Correct approach: Consider all special roads without filtering:

for road_start_x, road_start_y, road_end_x, road_end_y, road_cost in specialRoads:
    # Consider all roads regardless of their individual efficiency
    cost_to_road_start = calculate_manhattan_distance(
        current_x, current_y, road_start_x, road_start_y
    )
    total_cost = current_cost + cost_to_road_start + road_cost
    heappush(priority_queue, (total_cost, road_end_x, road_end_y))

3. Visiting Special Road Endpoints Only

Pitfall: Only adding special road start and end points to the graph, missing the fact that you can walk to any special road's starting point from any position.

Why it happens: When thinking of this as a graph problem, developers might pre-build a graph with only special road endpoints as nodes, missing potential optimal paths.

Incorrect approach:

# Wrong: Pre-building a limited graph
nodes = {(start[0], start[1]), (target[0], target[1])}
for x1, y1, x2, y2, cost in specialRoads:
    nodes.add((x1, y1))
    nodes.add((x2, y2))

# Then only considering paths between these fixed nodes

Correct approach: The solution correctly considers walking from any position to any special road's starting point, allowing for more flexible pathfinding.

Discover Your Strengths and Weaknesses: Take Our 5-Minute Quiz to Tailor Your Study Plan:

What's the output of running the following function using input [30, 20, 10, 100, 33, 12]?

1def fun(arr: List[int]) -> List[int]:
2    import heapq
3    heapq.heapify(arr)
4    res = []
5    for i in range(3):
6        res.append(heapq.heappop(arr))
7    return res
8
1public static int[] fun(int[] arr) {
2    int[] res = new int[3];
3    PriorityQueue<Integer> heap = new PriorityQueue<>();
4    for (int i = 0; i < arr.length; i++) {
5        heap.add(arr[i]);
6    }
7    for (int i = 0; i < 3; i++) {
8        res[i] = heap.poll();
9    }
10    return res;
11}
12
1class HeapItem {
2    constructor(item, priority = item) {
3        this.item = item;
4        this.priority = priority;
5    }
6}
7
8class MinHeap {
9    constructor() {
10        this.heap = [];
11    }
12
13    push(node) {
14        // insert the new node at the end of the heap array
15        this.heap.push(node);
16        // find the correct position for the new node
17        this.bubble_up();
18    }
19
20    bubble_up() {
21        let index = this.heap.length - 1;
22
23        while (index > 0) {
24            const element = this.heap[index];
25            const parentIndex = Math.floor((index - 1) / 2);
26            const parent = this.heap[parentIndex];
27
28            if (parent.priority <= element.priority) break;
29            // if the parent is bigger than the child then swap the parent and child
30            this.heap[index] = parent;
31            this.heap[parentIndex] = element;
32            index = parentIndex;
33        }
34    }
35
36    pop() {
37        const min = this.heap[0];
38        this.heap[0] = this.heap[this.size() - 1];
39        this.heap.pop();
40        this.bubble_down();
41        return min;
42    }
43
44    bubble_down() {
45        let index = 0;
46        let min = index;
47        const n = this.heap.length;
48
49        while (index < n) {
50            const left = 2 * index + 1;
51            const right = left + 1;
52
53            if (left < n && this.heap[left].priority < this.heap[min].priority) {
54                min = left;
55            }
56            if (right < n && this.heap[right].priority < this.heap[min].priority) {
57                min = right;
58            }
59            if (min === index) break;
60            [this.heap[min], this.heap[index]] = [this.heap[index], this.heap[min]];
61            index = min;
62        }
63    }
64
65    peek() {
66        return this.heap[0];
67    }
68
69    size() {
70        return this.heap.length;
71    }
72}
73
74function fun(arr) {
75    const heap = new MinHeap();
76    for (const x of arr) {
77        heap.push(new HeapItem(x));
78    }
79    const res = [];
80    for (let i = 0; i < 3; i++) {
81        res.push(heap.pop().item);
82    }
83    return res;
84}
85

Recommended Readings

Want a Structured Path to Master System Design Too? Don’t Miss This!

Load More