Want Help Cracking FAANG?

(Then click this)

×
Back to Question Bank

1801. Number of Orders in the Backlog - Leetcode Solution

Problem Description

The "Number of Orders in the Backlog" problem asks you to process a sequence of buy and sell orders in a stock market simulation. Each order is represented by an array of three integers: [price, amount, orderType], where orderType is 0 for a buy order and 1 for a sell order.

The rules are:

  • Buy orders can be matched with the lowest-priced sell orders in the backlog, as long as the buy price is greater than or equal to the sell price.
  • Sell orders can be matched with the highest-priced buy orders in the backlog, as long as the sell price is less than or equal to the buy price.
  • When an order can't be fully matched, the remaining part is added to the backlog.
  • At the end, you must return the total number of remaining orders in the backlog, modulo 10^9 + 7.
Constraints:
  • Each order is processed in the given order.
  • Each buy/sell order can only be matched with one or more orders of the opposite type.
  • Do not reuse elements; orders are matched and removed as they are fulfilled.

Thought Process

At first glance, the problem seems to require matching orders in a straightforward manner. A brute-force approach would involve scanning all existing orders in the backlog to find matches for each new order, but this would be inefficient for large inputs.

To optimize, we need a way to always quickly access the best possible match for any incoming order:

  • For a buy order, we want to match it with the lowest-priced sell orders (since a buyer wants to pay as little as possible).
  • For a sell order, we want to match it with the highest-priced buy orders (since a seller wants to get as much as possible).
This suggests using specialized data structures that can efficiently give us the minimum or maximum price orders at any time.

Solution Approach

We'll use two heaps (priority queues) to efficiently manage and access the orders in the backlog:

  • Min-heap for sell orders: allows us to quickly get the sell order with the lowest price.
  • Max-heap for buy orders: allows us to quickly get the buy order with the highest price.

  1. Initialize two heaps: one for buy orders (max-heap), one for sell orders (min-heap).
  2. Process each order in the input array:
    • If it's a buy order:
      • While there is a sell order in the backlog with a price less than or equal to the buy price and the amount is not zero:
        • Match as much as possible: subtract the smaller of the buy/sell amounts from both, and remove the order if its amount drops to zero.
      • If any amount remains, add it to the buy backlog.
    • If it's a sell order:
      • While there is a buy order in the backlog with a price greater than or equal to the sell price and the amount is not zero:
        • Match as much as possible: subtract the smaller of the buy/sell amounts from both, and remove the order if its amount drops to zero.
      • If any amount remains, add it to the sell backlog.
  3. After processing all orders, sum the amounts of all remaining orders in both backlogs and return the result modulo 10^9 + 7.

We use heaps because they allow us to always access the "best" order to match in logarithmic time, rather than searching through all orders.

Example Walkthrough

Sample Input:

orders = [
  [10, 5, 0],  // buy 5 at price 10
  [15, 2, 1],  // sell 2 at price 15
  [25, 1, 1],  // sell 1 at price 25
  [30, 4, 0]   // buy 4 at price 30
]
    
Step-by-step:
  1. Process [10, 5, 0] (buy 5 at 10): No sell orders to match, add to buy backlog.
  2. Process [15, 2, 1] (sell 2 at 15): Buy order at 10 is less than 15, so no match. Add to sell backlog.
  3. Process [25, 1, 1] (sell 1 at 25): Highest buy is 10, still less than 25. Add to sell backlog.
  4. Process [30, 4, 0] (buy 4 at 30): Lowest sell is 15 (2 units).
    • Match 2 units at 15: buy amount left = 2
    • Next sell is at 25 (1 unit): match 1, buy amount left = 1
    • No more sell orders at price ≤ 30. Add remaining buy order (1 at 30) to buy backlog.
Backlog after all orders:
  • Buy orders: [10, 5, 0] now has 5 units (unchanged), [30, 1, 0] (from last unmatched buy)
  • Sell orders: none left
Total backlog orders = 5 (from [10,5,0]) + 1 (from [30,1,0]) = 6

Time and Space Complexity

Brute-force approach:

  • Each new order could scan all existing backlog orders: O(n^2) time in the worst case.
  • Space: O(n) for storing all orders.
Optimized (heap-based) approach:
  • Each order is pushed/popped from a heap: O(log n) per operation.
  • Each order is processed once, so total time: O(n log n).
  • Space: O(n) for storing the backlogs in heaps.

The use of heaps ensures efficient access to the best matching order and keeps the solution scalable for large inputs.

Summary

We efficiently solve the "Number of Orders in the Backlog" problem by using two heaps to always access the best matching buy or sell order for each incoming order. This approach avoids brute-force scanning and leverages the properties of heaps for quick access and updates, resulting in an elegant and scalable solution. The key insight is to process orders greedily using appropriate data structures to maintain the backlog efficiently.

Code Implementation

import heapq

class Solution:
    def getNumberOfBacklogOrders(self, orders):
        MOD = 10**9 + 7
        buy_heap = []  # max-heap (invert price)
        sell_heap = [] # min-heap

        for price, amount, orderType in orders:
            if orderType == 0:
                # Buy order
                while amount > 0 and sell_heap and sell_heap[0][0] <= price:
                    sell_price, sell_amount = heapq.heappop(sell_heap)
                    matched = min(amount, sell_amount)
                    amount -= matched
                    sell_amount -= matched
                    if sell_amount > 0:
                        heapq.heappush(sell_heap, (sell_price, sell_amount))
                if amount > 0:
                    heapq.heappush(buy_heap, (-price, amount))
            else:
                # Sell order
                while amount > 0 and buy_heap and -buy_heap[0][0] >= price:
                    buy_price, buy_amount = heapq.heappop(buy_heap)
                    matched = min(amount, buy_amount)
                    amount -= matched
                    buy_amount -= matched
                    if buy_amount > 0:
                        heapq.heappush(buy_heap, (buy_price, buy_amount))
                if amount > 0:
                    heapq.heappush(sell_heap, (price, amount))

        total = 0
        for _, amount in buy_heap:
            total = (total + amount) % MOD
        for _, amount in sell_heap:
            total = (total + amount) % MOD
        return total
      
#include <vector>
#include <queue>
using namespace std;

class Solution {
public:
    int getNumberOfBacklogOrders(vector<vector<int>>& orders) {
        const int MOD = 1e9 + 7;
        // max-heap for buy: highest price first
        priority_queue<pair<int, int>> buy;
        // min-heap for sell: lowest price first
        priority_queue<pair<int, int>, vector<pair<int, int>>, greater<pair<int, int>>> sell;

        for (auto& order : orders) {
            int price = order[0], amount = order[1], type = order[2];
            if (type == 0) {
                // Buy order
                while (amount > 0 && !sell.empty() && sell.top().first <= price) {
                    auto [sellPrice, sellAmount] = sell.top();
                    sell.pop();
                    int matched = min(amount, sellAmount);
                    amount -= matched;
                    sellAmount -= matched;
                    if (sellAmount > 0) sell.push({sellPrice, sellAmount});
                }
                if (amount > 0) buy.push({price, amount});
            } else {
                // Sell order
                while (amount > 0 && !buy.empty() && buy.top().first >= price) {
                    auto [buyPrice, buyAmount] = buy.top();
                    buy.pop();
                    int matched = min(amount, buyAmount);
                    amount -= matched;
                    buyAmount -= matched;
                    if (buyAmount > 0) buy.push({buyPrice, buyAmount});
                }
                if (amount > 0) sell.push({price, amount});
            }
        }
        long long total = 0;
        while (!buy.empty()) {
            total = (total + buy.top().second) % MOD;
            buy.pop();
        }
        while (!sell.empty()) {
            total = (total + sell.top().second) % MOD;
            sell.pop();
        }
        return (int)total;
    }
};
      
import java.util.*;

class Solution {
    public int getNumberOfBacklogOrders(int[][] orders) {
        int MOD = 1_000_000_007;
        // Max-heap for buy orders
        PriorityQueue<int[]> buy = new PriorityQueue<>((a, b) -> b[0] - a[0]);
        // Min-heap for sell orders
        PriorityQueue<int[]> sell = new PriorityQueue<>((a, b) -> a[0] - b[0]);

        for (int[] order : orders) {
            int price = order[0], amount = order[1], type = order[2];
            if (type == 0) {
                // Buy order
                while (amount > 0 && !sell.isEmpty() && sell.peek()[0] <= price) {
                    int[] top = sell.poll();
                    int matched = Math.min(amount, top[1]);
                    amount -= matched;
                    top[1] -= matched;
                    if (top[1] > 0) sell.offer(top);
                }
                if (amount > 0) buy.offer(new int[]{price, amount});
            } else {
                // Sell order
                while (amount > 0 && !buy.isEmpty() && buy.peek()[0] >= price) {
                    int[] top = buy.poll();
                    int matched = Math.min(amount, top[1]);
                    amount -= matched;
                    top[1] -= matched;
                    if (top[1] > 0) buy.offer(top);
                }
                if (amount > 0) sell.offer(new int[]{price, amount});
            }
        }
        long total = 0;
        for (int[] order : buy) total = (total + order[1]) % MOD;
        for (int[] order : sell) total = (total + order[1]) % MOD;
        return (int)total;
    }
}
      
class Heap {
    constructor(cmp) {
        this.data = [];
        this.cmp = cmp;
    }
    push(val) {
        this.data.push(val);
        this._up(this.data.length - 1);
    }
    pop() {
        if (this.size() === 0) return null;
        const top = this.data[0];
        const last = this.data.pop();
        if (this.data.length > 0) {
            this.data[0] = last;
            this._down(0);
        }
        return top;
    }
    peek() {
        return this.data[0];
    }
    size() {
        return this.data.length;
    }
    _up(idx) {
        while (idx > 0) {
            let parent = (idx - 1) >> 1;
            if (this.cmp(this.data[idx], this.data[parent]) < 0) {
                [this.data[idx], this.data[parent]] = [this.data[parent], this.data[idx]];
                idx = parent;
            } else break;
        }
    }
    _down(idx) {
        let n = this.data.length;
        while (true) {
            let left = idx * 2 + 1, right = idx * 2 + 2, smallest = idx;
            if (left < n && this.cmp(this.data[left], this.data[smallest]) < 0) smallest = left;
            if (right < n && this.cmp(this.data[right], this.data[smallest]) < 0) smallest = right;
            if (smallest !== idx) {
                [this.data[idx], this.data[smallest]] = [this.data[smallest], this.data[idx]];
                idx = smallest;
            } else break;
        }
    }
}

var getNumberOfBacklogOrders = function(orders) {
    const MOD = 1e9 + 7;
    // Max-heap for buy (by price descending)
    let buy = new Heap((a, b) => b[0] - a[0]);
    // Min-heap for sell (by price ascending)
    let sell = new Heap((a, b) => a[0] - b[0]);
    for (let [price, amount, type] of orders) {
        if (type === 0) {
            // Buy order
            while (amount > 0 && sell.size() && sell.peek()[0] <= price) {
                let [sellPrice, sellAmount] = sell.pop();
                let matched = Math.min(amount, sellAmount);
                amount -= matched;
                sellAmount -= matched;
                if (sellAmount > 0) sell.push([sellPrice, sellAmount]);
            }
            if (amount > 0) buy.push([price, amount]);
        } else {
            // Sell order
            while (amount > 0 && buy.size() && buy.peek()[0] >= price) {
                let [buyPrice, buyAmount] = buy.pop();
                let matched = Math.min(amount, buyAmount);
                amount -= matched;
                buyAmount -= matched;
                if (buyAmount > 0) buy.push([buyPrice, buyAmount]);
            }
            if (amount > 0) sell.push([price, amount]);
        }
    }
    let total = 0;
    for (let [_, amt] of buy.data) total = (total + amt) % MOD;
    for (let [_, amt] of sell.data) total = (total + amt) % MOD;
    return total;
};