Design Browser History

Expert Answer & Key Takeaways

# 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)O(1) read and O(N)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)O(1) caching (LRU/LFU). ```text [Head] <-> [Node A] <-> [Node B] <-> [Node C] <-> [Tail] ^ ^ ^ ^ ^ (MRU) (Data) (Data) (Data) (LRU) ``` - HashMap: Provides O(1)O(1) lookups for keys to their corresponding nodes. - DLL: Provides O(1)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)O(1), and "flipping" elements to another stack happens only when necessary, averaging O(1)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 Browser History

You have a browser of one tab where you start on a homepage and you can visit another url, get back in the history number of steps or move forward in the history number of steps.
Medium
Approach 1

Level I: Two Stacks

Intuition

Maintain history and forward stacks. When visiting, clear forward stack.
O(1) all ops.💾 O(N).

Detailed Dry Run

Visit(A), Visit(B), Back(1) -> Back is at A.
java
import java.util.*;\nclass BrowserHistory {\n    private Stack<String> history = new Stack<>(), forward = new Stack<>();\n    public BrowserHistory(String homepage) { history.push(homepage); }\n    public void visit(String url) { history.push(url); forward.clear(); }\n    public String back(int steps) {\n        while(steps-- > 0 && history.size() > 1) forward.push(history.pop());\n        return history.peek();\n    }\n    public String forward(int steps) {\n        while(steps-- > 0 && !forward.isEmpty()) history.push(forward.pop());\n        return history.peek();\n    }\n}
Approach 2

Level III: Single Array with Pointer (Optimal)

Intuition

Use a single dynamic array (list) and an index pointer. Overwrite forward history on visit and simply move the index on back / forward.
O(1) average all ops.💾 O(N).

Detailed Dry Run

Index starts at 0. Visit moves it to 1. Back(1) moves it to 0.
java
import java.util.*;\nclass BrowserHistory {\n    private List<String> history = new ArrayList<>();\n    private int curr = 0, last = 0;\n    public BrowserHistory(String homepage) { history.add(homepage); }\n    public void visit(String url) {\n        curr++;\n        if(curr < history.size()) history.set(curr, url);\n        else history.add(url);\n        last = curr;\n    }\n    public String back(int steps) { curr = Math.max(0, curr - steps); return history.get(curr); }\n    public String forward(int steps) { curr = Math.min(last, curr + steps); return history.get(curr); }\n}

Course4All Technical Board

Verified Expert

Senior Software Engineers & Algorithmic Experts

Our DSA content is authored and reviewed by engineers from top tech firms to ensure optimal time and space complexity analysis.

Pattern: 2026 Ready
Updated: Weekly