Home/dsa/Design/Design Movie Rental System

Design Movie Rental System

# System & Data Structure Design Design problems in DSA interviews test your ability to translate requirements into a functional, efficient, and maintainable class structure. Unlike standard algorithmic problems, the focus here is on **State Management** and **API Design**. ### Core Principles 1. **Encapsulation:** Keep data private and expose functionality through well-defined methods. 2. **Trade-offs:** Every design choice has a cost. Is it better to have $O(1)$ read and $O(N)$ write, or vice versa? 3. **State Consistency:** Ensure that your internal data structures (e.g., a Map and a List) stay in sync after every operation. ### Common Design Patterns #### 1. HashMap + Doubly Linked List (DLL) The "Gold Standard" for $O(1)$ caching (LRU/LFU). ```text [Head] <-> [Node A] <-> [Node B] <-> [Node C] <-> [Tail] ^ ^ ^ ^ ^ (MRU) (Data) (Data) (Data) (LRU) ``` - **HashMap:** Provides $O(1)$ lookups for keys to their corresponding nodes. - **DLL:** Provides $O(1)$ addition/removal of nodes at both ends, maintaining the order of access. #### 2. Amortized Analysis (Rebalancing) Commonly used in **Queue using Stacks** or **Dynamic Arrays**. - Instead of doing heavy work on every call, we batch it. Pushing to a stack is $O(1)$, and "flipping" elements to another stack happens only when necessary, averaging $O(1)$ per operation. #### 3. Ring Buffers (Circular Arrays) Used for fixed-size memory management (e.g., **Circular Queue**, **Hit Counter**). ```text [0] [1] [2] [3] [4] [5] ^ ^ ^ Head (Data) Tail (Pops) (Next Push) ``` - Use `(index + 1) % capacity` to wrap around the array. #### 4. Concurrency & Thread Safety For "Hard" design problems (e.g., **Bounded Blocking Queue**). - Use **Mutexes** (Locks) to prevent data races. - Use **Condition Variables** (`wait`/`notify`) to manage producer-consumer logic efficiently without busy-waiting. ### How to Approach a Design Problem 1. **Identify the API:** What methods do you need to implement? (`get`, `put`, `push`, etc.) 2. **Define the State:** What variables represent the current state? (Size, Capacity, Pointers). 3. **Choose the Data Structures:** Select the combination that minimizes time complexity for the most frequent operations. 4. **Dry Run:** Trace the state changes through a sequence of operations based on your chosen structure.

Design Movie Rental System

Design a movie rental system that supports searching for available movies, renting, dropping off, and reporting the cheapest rented movies.

Hard
Approach 1

Level III: Multi-Index Sorted Sets (Optimal)

Intuition

Use multiple data structures to track state: available[movie] -> SortedSet(price, shop), rented -> SortedSet(price, shop, movie), and priceMap[shop, movie] -> price.

Search/Report: O(1) or O(K log N), Rent/Drop: O(log N).💾 O(N).

Detailed Dry Run

Rent movie 1 at shop 2. Move (price, shop, movie) from available to rented set.

java
import java.util.*;\nclass MovieRentingSystem {\n    class Movie implements Comparable<Movie> {\n        int shop, movie, price;\n        Movie(int s, int m, int p) { shop=s; movie=m; price=p; }\n        public int compareTo(Movie o) {\n            if(price != o.price) return price-o.price;\n            if(shop != o.shop) return shop-o.shop;\n            return movie-o.movie;\n        }\n    }\n    Map<Integer, TreeSet<Movie>> available = new HashMap<>();\n    TreeSet<Movie> rented = new TreeSet<>();\n    Map<String, Integer> priceMap = new HashMap<>();\n    public MovieRentingSystem(int n, int[][] entries) {\n        for(int[] e : entries) {\n            Movie m = new Movie(e[0], e[1], e[2]);\n            available.computeIfAbsent(e[1], k -> new TreeSet<>()).add(m);\n            priceMap.put(e[0]+","+e[1], e[2]);\n        }\n    }\n    public List<Integer> search(int movie) {\n        List<Integer> res = new ArrayList<>();\n        if(!available.containsKey(movie)) return res;\n        for(Movie m : available.get(movie)) { res.add(m.shop); if(res.size()==5) break; }\n        return res;\n    }\n    public void rent(int shop, int movie) {\n        int p = priceMap.get(shop+","+movie);\n        Movie m = new Movie(shop, movie, p);\n        available.get(movie).remove(m);\n        rented.add(m);\n    }\n    public void drop(int shop, int movie) {\n        int p = priceMap.get(shop+","+movie);\n        Movie m = new Movie(shop, movie, p);\n        rented.remove(m);\n        available.get(movie).add(m);\n    }\n    public List<List<Integer>> report() {\n        List<List<Integer>> res = new ArrayList<>();\n        for(Movie m : rented) { res.add(Arrays.asList(m.shop, m.movie)); if(res.size()==5) break; }\n        return res;\n    }\n}