· 9 min read

LeetCode for Travel-Booking Platforms: Easy & Medium Patterns (JS)

A pragmatic, domain-mapped LeetCode guide for Easy/Medium problems you’ll see in travel-booking style systems: intervals/availability, sliding windows, heaps, hashing, prefix sums, and binary search—with clean JavaScript templates.

This is a focused set of Easy/Medium LeetCode problems that map cleanly to travel-booking platform needs: availability/rooms, inventory windows, pricing, ranking, capacity, and caching. Each topic has a one-sentence mapping plus a JS template you can re-use.

Quick Map (Problem → Scenario → Technique)

  • Can Attend Meetings → back-to-back booking validation → sort + scan
  • Min Rooms (meeting/hotel rooms) → concurrent bookings → two pointers on starts/ends
  • Merge/Insert Intervals → consolidate booking ranges → sort by start + greedy merge
  • Corporate Flight Bookings → batched updates on routes/dates → difference array + prefix sum
  • Car Pooling → capacity feasibility per corridor/time → event sweep (diff array)
  • Top K Frequent Elements → top searched cities/hotels → hashmap + bucket/heap
  • Min Size Subarray Sum → minimum window hitting a target demand → sliding window
  • Max Average Subarray I → rolling KPI (occupancy/price) → fixed window
  • Search Insert Position → price bucket insertion / sorted ids → lower_bound binary search
  • Two Sum → bundle pricing / complementary offers → hashmap lookup
  • Merge Sorted Array → merge paginated, sorted search results → in-place from back
  • LRU Cache → cache hot searches/results → ordered Map

Availability & Intervals

Can Attend All Meetings (No Overlap) — Easy

Problem. Given intervals[i] = [start, end) determine if any overlap exists.

  • Input/Output: number[][] → boolean
  • Mapping: Validate that back-to-back bookings are allowed but overlaps are rejected.
  • Approach: Sort by start; if start_i < end_{i-1} anywhere, there’s an overlap.
  • Complexity: Time O(n log n), Space O(1) (in-place sort).
  • Edge cases: Empty or single interval; equal end-start is allowed with half-open [start, end).
  • Example: [[10,20],[20,30]] → true, [[10,20],[19,25]] → false.
function canAttendMeetings(intervals) {
  intervals.sort((a, b) => a[0] - b[0]);
  for (let i = 1; i < intervals.length; i++) {
    if (intervals[i][0] < intervals[i - 1][1]) return false;
  }
  return true;
}

Minimum Number of Rooms (Concurrent Bookings) — Medium

Problem. Minimum parallel rooms to host all intervals without overlap.

  • Input/Output: number[][] → number
  • Mapping: Peak concurrent bookings = number of rooms/capacity needed.
  • Approach: Sort starts and ends separately; sweep with two pointers; increment when a meeting starts before the earliest end, else decrement.
  • Complexity: Time O(n log n), Space O(n) for starts/ends.
  • Edge cases: Same-time start/end; treat end before start at same timestamp to free capacity.
  • Example: [[0,30],[5,10],[15,20]] → 2.
function minMeetingRooms(intervals) {
  if (intervals.length === 0) return 0;
  const starts = intervals.map((i) => i[0]).sort((a, b) => a - b);
  const ends = intervals.map((i) => i[1]).sort((a, b) => a - b);
  let i = 0,
    j = 0,
    used = 0,
    best = 0;
  while (i < starts.length) {
    if (starts[i] < ends[j]) {
      used++;
      if (used > best) best = used;
      i++;
    } else {
      used--;
      j++;
    }
  }
  return best;
}

Merge Intervals — Medium

Problem. Merge all overlapping intervals.

  • Input/Output: number[][] → number[][]
  • Mapping: Combine adjacent availability windows into compact blocks.
  • Approach: Sort by start; push or merge with last interval greedily.
  • Complexity: Time O(n log n), Space O(n).
  • Edge cases: Fully contained intervals; identical starts; back-to-back [a,b) and [b,c) remain separate under half-open semantics.
  • Example: [[1,3],[2,6],[8,10],[15,18]] → [[1,6],[8,10],[15,18]].
function mergeIntervals(intervals) {
  if (intervals.length <= 1) return intervals;
  intervals.sort((a, b) => a[0] - b[0]);
  const res = [intervals[0].slice()];
  for (let i = 1; i < intervals.length; i++) {
    const [s, e] = intervals[i];
    const last = res[res.length - 1];
    if (s <= last[1]) last[1] = Math.max(last[1], e);
    else res.push([s, e]);
  }
  return res;
}

Insert Interval — Medium

Problem. Insert a new interval into non-overlapping sorted intervals and merge as needed.

  • Input/Output: (number[][], [number, number]) → number[][]
  • Mapping: Add a new availability/blackout and normalize the calendar.
  • Approach: Append all intervals ending before new; merge overlaps; append the rest.
  • Complexity: Time O(n), Space O(1) extra if reusing output buffer.
  • Edge cases: New interval before all; after all; fully covering many.
  • Example: [[1,3],[6,9]], [2,5] → [[1,5],[6,9]].
function insertInterval(intervals, newInterval) {
  const res = [];
  let [ns, ne] = newInterval;
  let i = 0;
  while (i < intervals.length && intervals[i][1] < ns) res.push(intervals[i++]);
  while (i < intervals.length && intervals[i][0] <= ne) {
    ns = Math.min(ns, intervals[i][0]);
    ne = Math.max(ne, intervals[i][1]);
    i++;
  }
  res.push([ns, ne]);
  while (i < intervals.length) res.push(intervals[i++]);
  return res;
}

Batched Updates & Capacity

Corporate Flight Bookings — Medium

Problem. Apply batch additions over date ranges efficiently.

  • Input/Output: bookings: [start,end,seats][], n: number → number[]
  • Mapping: Promotions/load updates over date windows per route; compute resulting demand per day.
  • Approach: Difference array; add at start, subtract after end; one prefix pass to realize totals.
  • Complexity: Time O(n + q), Space O(n).
  • Edge cases: 1-indexed ranges; very large q; overlapping updates canceling.
  • Example: [[1,2,10],[2,3,20]], n=3 → [10,30,20].
// bookings: number[][] where [start, end, seats] (1-indexed days)
// n: number of days
function corpFlightBookings(bookings, n) {
  const diff = new Array(n + 1).fill(0);
  for (const [start, end, seats] of bookings) {
    diff[start - 1] += seats;
    diff[end] -= seats;
  }
  const res = new Array(n).fill(0);
  let cur = 0;
  for (let i = 0; i < n; i++) {
    cur += diff[i];
    res[i] = cur;
  }
  return res;
}

Car Pooling — Medium

Problem. Determine if trips fit into capacity with pickups/dropoffs along a line.

  • Input/Output: trips: [passengers, start, end][], capacity: number → boolean
  • Mapping: Shuttle capacity across segments/time windows with loads and unloads.
  • Approach: Event sweep: +p at start, -p at end; sort by time, apply cumulative sum; ensure never exceeds capacity.
  • Complexity: Time O(n log n), Space O(n).
  • Edge cases: Equal times (process drop before pick); large passenger counts; zero-capacity sanity.
  • Example: [[2,1,5],[3,3,7]], cap=4 → false.
// trips: [passengers, start, end]
function carPooling(trips, capacity) {
  const events = [];
  for (const [p, s, e] of trips) {
    events.push([s, p]);
    events.push([e, -p]);
  }
  events.sort((a, b) => (a[0] === b[0] ? a[1] - b[1] : a[0] - b[0]));
  let cur = 0;
  for (const [, d] of events) {
    cur += d;
    if (cur > capacity) return false;
  }
  return true;
}

Ranking & Frequency

Top K Frequent Elements — Medium

Problem. Return the k most frequent items.

  • Input/Output: (number[]|string[], k: number) → same-type[]
  • Mapping: Top searched cities/hotels/filters.
  • Approach: Count with hashmap; bucket by frequency; collect from highest bucket down. Alt: min-heap of size k.
  • Complexity: Bucket: Time O(n), Space O(n); Heap: Time O(n log k).
  • Edge cases: Ties; k equals distinct count; large domain values.
  • Example: nums=[1,1,1,2,2,3], k=2 → [1,2].
function topKFrequent(nums, k) {
  const count = new Map();
  for (const x of nums) count.set(x, (count.get(x) || 0) + 1);
  const buckets = Array(nums.length + 1)
    .fill(0)
    .map(() => []);
  for (const [val, freq] of count) buckets[freq].push(val);
  const res = [];
  for (let f = buckets.length - 1; f >= 0 && res.length < k; f--) {
    for (const v of buckets[f]) {
      res.push(v);
      if (res.length === k) break;
    }
  }
  return res;
}

Sliding Windows (Demand, KPIs)

Minimum Size Subarray Sum — Medium

Problem. Smallest contiguous window whose sum ≥ target.

  • Input/Output: (target: number, nums: number[]) → number
  • Mapping: Minimal number of days to hit a sales/demand threshold.
  • Approach: Expand right; while sum ≥ target, shrink left and update best.
  • Complexity: Time O(n), Space O(1).
  • Edge cases: No solution (return 0); large numbers; single element ≥ target.
  • Example: target=7, nums=[2,3,1,2,4,3] → 2 ([4,3]).
function minSubArrayLen(target, nums) {
  let left = 0,
    sum = 0,
    best = Infinity;
  for (let right = 0; right < nums.length; right++) {
    sum += nums[right];
    while (sum >= target) {
      best = Math.min(best, right - left + 1);
      sum -= nums[left++];
    }
  }
  return best === Infinity ? 0 : best;
}

Maximum Average Subarray I — Easy

Problem. Maximize average over any length-k window.

  • Input/Output: (nums: number[], k: number) → number
  • Mapping: Highest average KPI (e.g., occupancy) over fixed horizon.
  • Approach: Sum first k; slide by add-right, remove-left while tracking best.
  • Complexity: Time O(n), Space O(1).
  • Edge cases: k equals n; negatives; decimals.
  • Example: nums=[1,12,-5,-6,50,3], k=4 → 12.75.
function findMaxAverage(nums, k) {
  let sum = 0;
  for (let i = 0; i < k; i++) sum += nums[i];
  let best = sum;
  for (let i = k; i < nums.length; i++) {
    sum += nums[i] - nums[i - k];
    if (sum > best) best = sum;
  }
  return best / k;
}

Binary Search (Sorted Data/Prices)

Search Insert Position — Easy

Problem. Position to insert target in sorted array.

  • Input/Output: (nums: number[], target: number) → number
  • Mapping: Insert price tier or id into sorted catalog.
  • Approach: Lower-bound binary search: first index where nums[i] ≥ target.
  • Complexity: Time O(log n), Space O(1).
  • Edge cases: Insert at start/end; duplicates (return first occurrence).
  • Example: nums=[1,3,5,6], target=5 → 2, target=2 → 1, target=7 → 4.
function searchInsert(nums, target) {
  let lo = 0,
    hi = nums.length;
  while (lo < hi) {
    const mid = lo + ((hi - lo) >> 1);
    if (nums[mid] < target) lo = mid + 1;
    else hi = mid;
  }
  return lo;
}

Hashing & Merging (Catalogs)

Two Sum — Easy

Problem. Find two indices whose values sum to target.

  • Input/Output: (nums: number[], target: number) → [number, number]
  • Mapping: Complementary bundle pricing or promo targets.
  • Approach: Hashmap of value → index; for each value check if complement seen.
  • Complexity: Time O(n), Space O(n).
  • Edge cases: Duplicates; negative targets; exactly one solution per constraints.
  • Example: nums=[2,7,11,15], target=9 → [0,1].
function twoSum(nums, target) {
  const seen = new Map();
  for (let i = 0; i < nums.length; i++) {
    const need = target - nums[i];
    if (seen.has(need)) return [seen.get(need), i];
    seen.set(nums[i], i);
  }
  return [];
}

Merge Sorted Array — Easy

Problem. Merge two sorted arrays where the first has trailing buffer.

  • Input/Output: (nums1: number[], m: number, nums2: number[], n: number) → void
  • Mapping: Merge two sorted result pages into one stream in-place.
  • Approach: Fill from the back comparing tails to avoid overwriting.
  • Complexity: Time O(m+n), Space O(1).
  • Edge cases: One empty; all from nums2 smaller/larger.
  • Example: nums1=[1,2,3,0,0,0], m=3, nums2=[2,5,6], n=3 → [1,2,2,3,5,6].
// nums1 has length m + n with trailing space; merge nums2 into nums1
function merge(nums1, m, nums2, n) {
  let i = m - 1,
    j = n - 1,
    k = m + n - 1;
  while (j >= 0) {
    if (i >= 0 && nums1[i] > nums2[j]) nums1[k--] = nums1[i--];
    else nums1[k--] = nums2[j--];
  }
}

Caching Hot Results

LRU Cache — Medium

Problem. Design a cache that evicts least-recently-used on capacity.

  • Input/Output: get(key): number, put(key, value): void
  • Mapping: Cache hot destination/hotel search results with bounded memory.
  • Approach: Ordered Map: reinsert on get/put to mark recent; evict oldest key when size exceeds capacity.
  • Complexity: Amortized O(1) for get/put.
  • Edge cases: Update existing keys; capacity = 1; non-existent keys.
  • Example: put(1,1); put(2,2); get(1) → 1; put(3,3) evicts 2.
class LRUCache {
  constructor(capacity) {
    this.capacity = capacity;
    this.map = new Map();
  }
  get(key) {
    if (!this.map.has(key)) return -1;
    const value = this.map.get(key);
    this.map.delete(key);
    this.map.set(key, value);
    return value;
  }
  put(key, value) {
    if (this.map.has(key)) this.map.delete(key);
    this.map.set(key, value);
    if (this.map.size > this.capacity) {
      const oldestKey = this.map.keys().next().value;
      this.map.delete(oldestKey);
    }
  }
}

Notes & Pitfalls

  • Prefer half-open intervals [start, end) to allow back-to-back bookings; adjust only if the problem states inclusive end.
  • For concurrency (rooms), sorting starts/ends separately with two pointers is often simpler and faster than heap-based solutions.
  • For bulk updates on ranges (routes/dates), difference arrays with one prefix pass turn O(n·q) into O(n + q).
  • For ranking (top K), bucket sort avoids log factors and is easy to code in JS.
  • Sliding windows: grow right, shrink left while constraint holds; track best window length/score.
  • Binary search for positions uses lower_bound (first index with nums[i] ≥ target); template above is safe for duplicates.
Back to Blog