Bird
Raised Fist0
Interview Prepcustom-data-structuresmediumAmazonGoogleFacebook

Snapshot Array

Choose your preparation mode4 modes available
📋
Problem

Imagine you are building a version control system for an array where you can take snapshots and query historical values efficiently.

Design a SnapshotArray that supports the following interface: - SnapshotArray(int length): Initializes an array-like data structure with the given length. Initially, each element equals 0. - void set(index, val): Sets the element at the given index to be equal to val. - int snap(): Takes a snapshot of the array and returns the snap_id: the total number of times snap() has been called minus 1. - int get(index, snap_id): Returns the value at the given index, at the time the snapshot with snap_id was taken.

1 ≤ length ≤ 10^5At most 5 * 10^4 calls will be made to set, snap, and get.0 ≤ index < length0 ≤ snap_id < number of times snap() has been called0 ≤ val ≤ 10^9
Edge cases: Calling get on an index that was never set after initialization → returns 0Multiple sets on the same index before a snap → only the last set before snap is recordedCalling snap multiple times without any set in between → snapshots should be distinct
</>
IDE
class SnapshotArray: def __init__(self, length: int): ... def set(self, index: int, val: int) -> None: ... def snap(self) -> int: ... def get(self, index: int, snap_id: int) -> int: ...public class SnapshotArray { public SnapshotArray(int length) { // ... } public void set(int index, int val) { // ... } public int snap() { // ... } public int get(int index, int snap_id) { // ... } }class SnapshotArray { public: SnapshotArray(int length) { // ... } void set(int index, int val) { // ... } int snap() { // ... } int get(int index, int snap_id) { // ... } };class SnapshotArray { constructor(length) { // ... } set(index, val) { // ... } snap() { // ... } get(index, snap_id) { // ... } }
class SnapshotArray:
    def __init__(self, length: int):
        # Write your solution here
        pass
    def set(self, index: int, val: int) -> None:
        pass
    def snap(self) -> int:
        pass
    def get(self, index: int, snap_id: int) -> int:
        pass
public class SnapshotArray {
    public SnapshotArray(int length) {
        // Write your solution here
    }
    public void set(int index, int val) {
    }
    public int snap() {
        return 0;
    }
    public int get(int index, int snap_id) {
        return 0;
    }
}
#include <vector>
using namespace std;

class SnapshotArray {
public:
    SnapshotArray(int length) {
        // Write your solution here
    }
    void set(int index, int val) {
    }
    int snap() {
        return 0;
    }
    int get(int index, int snap_id) {
        return 0;
    }
};
class SnapshotArray {
    constructor(length) {
        // Write your solution here
    }
    set(index, val) {
    }
    snap() {
        return 0;
    }
    get(index, snap_id) {
        return 0;
    }
}
Coming soon
0/9
Common Bugs to Avoid
Wrong: Returning the last set value regardless of snap_idNot storing historical values per snap_id, ignoring snapshot versions.✅ Store (snap_id, val) pairs per index and binary search on get for correct snap_id.
Wrong: Returning 0 for all gets even after sets and snapsNot recording set operations or snap increments properly.✅ Ensure snap increments snap_id and set records values per index.
Wrong: Returning earlier set value instead of last set before snapNot overwriting multiple sets before snap for same index.✅ Update last set value for current snap_id before snap is taken.
Wrong: Index out of range error or wrong value for invalid snap_idNot validating snap_id in get method.✅ Check snap_id bounds and return 0 or error if invalid.
Wrong: TLE on large inputsUsing full snapshot copies or linear search on get.✅ Use hash map per index with binary search for get to achieve O(log m).
✓
Test Cases
t1_01basic
Input{"length":3,"operations":[["set",0,5],["snap"],["set",0,6],["get",0,0]]}
Expected[0,5]

Initialize array length 3 with zeros. Set index 0 to 5. Snap returns snap_id=0. Set index 0 to 6. Get index 0 at snap_id 0 returns 5, the value at snapshot time.

t1_02basic
Input{"length":2,"operations":[["set",1,10],["snap"],["set",1,20],["snap"],["get",1,0],["get",1,1]]}
Expected[0,10,1,20]

Set index 1 to 10, snap returns 0. Set index 1 to 20, snap returns 1. Get index 1 at snap 0 returns 10, at snap 1 returns 20.

t2_01edge
Input{"length":1,"operations":[["snap"],["get",0,0]]}
Expected[0,0]

Array length 1 initialized with 0. Snap returns 0. Get index 0 at snap 0 returns 0 since no set was done.

t2_02edge
Input{"length":2,"operations":[["set",0,1],["set",0,2],["snap"],["get",0,0]]}
Expected[0,2]

Multiple sets on same index before snap; only last set (2) recorded for snap 0.

t2_03edge
Input{"length":1,"operations":[["snap"],["snap"],["snap"],["get",0,0],["get",0,1],["get",0,2]]}
Expected[0,0,1,0,2,0]

Multiple snaps without sets produce distinct snap_ids 0,1,2. Gets return 0 as no sets done.

t3_01corner
Input{"length":3,"operations":[["set",0,1],["snap"],["set",0,2],["snap"],["set",0,3],["get",0,0],["get",0,1],["get",0,2]]}
Expected[0,1,1,2,2,3]

Tests correct retrieval of values for multiple snaps with sets between snaps.

t3_02corner
Input{"length":2,"operations":[["set",0,5],["snap"],["set",0,10],["set",0,15],["snap"],["get",0,0],["get",0,1]]}
Expected[0,5,1,15]

Multiple sets before snap 1; only last set (15) recorded for snap 1.

t3_03corner
Input{"length":1,"operations":[["set",0,7],["snap"],["get",0,1]]}
Expected[0,7,0]

Get called with snap_id that does not exist (1) returns 0 or error handled gracefully.

t4_01performance
Input{"length":100,"operations":[["set",0,1],["snap"],["set",1,2],["snap"],["set",2,3],["snap"],["set",3,4],["snap"],["set",4,5],["snap"],["set",5,6],["snap"],["set",6,7],["snap"],["set",7,8],["snap"],["set",8,9],["snap"],["set",9,10],["snap"],["get",0,0],["get",1,1],["get",2,2],["get",3,3],["get",4,4],["get",5,5],["get",6,6],["get",7,7],["get",8,8],["get",9,9]]}
⏱ Performance - must finish in 2000ms

n=100, O(log m) per get with m snaps must complete within 2 seconds.

Practice

(1/5)
1. You need to design a data structure that supports incrementing and decrementing string keys, and retrieving a key with the maximum or minimum count, all in constant time. Which approach best guarantees O(1) time complexity for all these operations?
easy
A. Using a balanced binary search tree keyed by counts to maintain order of keys
B. Using a hash map to store counts and scanning all keys to find max/min when requested
C. Maintaining a doubly linked list of buckets, each bucket holding keys with the same count, plus hash maps for quick access
D. Using a priority queue to track max and min keys with lazy updates

Solution

  1. Step 1: Understand operation requirements

    Increment, decrement, getMaxKey, and getMinKey must all be O(1).
  2. Step 2: Evaluate approaches

    Hash map + linear scan (B) is O(n) for max/min. Balanced BST (C) and priority queue (D) have O(log n) operations. Doubly linked list with buckets and hash maps (A) supports O(1) updates and retrievals.
  3. Final Answer:

    Option C -> Option C
  4. Quick Check:

    Bucket list + hash maps enable O(1) increments, decrements, and max/min retrievals [OK]
Hint: Bucket list + hash maps enable O(1) max/min updates [OK]
Common Mistakes:
  • Thinking balanced BST or priority queue can achieve O(1) for all operations
2. Consider the following Python code snippet for the MedianFinder class using two heaps. After adding the numbers 1, 5, and 3 in that order, what is the value of the median returned by findMedian()?
easy
A. 1.0
B. 3.0
C. 5.0
D. 2.0

Solution

  1. Step 1: Insert 1

    Low heap empty, push -1 to low; low_size=1, high_size=0.
  2. Step 2: Insert 5

    5 > -low[0] (which is 1), push 5 to high; low_size=1, high_size=1; heaps balanced.
  3. Step 3: Insert 3

    3 <= 5 but > 1, push 3 to high; high_size=2, low_size=1; balance heaps by moving smallest from high (3) to low (-3); low_size=2, high_size=1.
  4. Step 4: Find median

    low_size > high_size, median is -low[0] = 3.0.
  5. Step 5: Re-examine median calculation

    After balancing, low heap has [-3, -1], high heap has [5]. Median is top of low heap = 3.0, but the question asks for median after adding 1,5,3 in order, so median is 3.0.
  6. Final Answer:

    Option B -> Option B
  7. Quick Check:

    Median after [1,5,3] is 3.0 [OK]
Hint: Median is top of larger heap after balancing [OK]
Common Mistakes:
  • Forgetting to balance heaps after insertion
  • Returning wrong heap top for median
3. The following code attempts to copy a linked list with random pointers using the optimal weaving approach. Identify the line that contains a subtle bug that can cause incorrect random pointer assignment or list corruption.
def copyRandomList(head):
    if not head:
        return None
    curr = head
    # Step 1: Insert copied nodes
    while curr:
        new_node = Node(curr.val, curr.next)
        curr.next = new_node
        curr = new_node.next
    # Step 2: Assign random pointers
    curr = head
    while curr:
        if curr.random:
            curr.next.random = curr.random  # Bug here
        curr = curr.next.next
    # Step 3: Separate lists
    curr = head
    copy_head = head.next
    copy_curr = copy_head
    while curr:
        curr.next = curr.next.next
        if copy_curr.next:
            copy_curr.next = copy_curr.next.next
        curr = curr.next
        copy_curr = copy_curr.next
    return copy_head
medium
A. Line assigning curr.next.random = curr.random
B. Line inserting copied nodes: curr.next = new_node
C. Line advancing curr in Step 1: curr = new_node.next
D. Line separating original and copied lists: curr.next = curr.next.next

Solution

  1. Step 1: Analyze random pointer assignment

    The line assigns curr.next.random = curr.random, but curr.random points to original nodes, not copied nodes.
  2. Step 2: Correct assignment

    It should be curr.next.random = curr.random.next to point to the copied node corresponding to curr.random.
  3. Final Answer:

    Option A -> Option A
  4. Quick Check:

    Incorrect random pointer assignment breaks copied list correctness [OK]
Hint: Random pointer must point to copied node, not original [OK]
Common Mistakes:
  • Assigning random pointer directly to original node
  • Mixing original and copied nodes after weaving
  • Forgetting to advance curr by two nodes
4. What is the time complexity of the checkOut operation in the optimal UndergroundSystem implementation that uses a linked list for check-ins, assuming n is the number of active check-ins at the time of checkout?
medium
A. O(1) because the linked list head always points to the correct check-in node
B. O(n) due to updating route data hash map entries
C. O(log n) because the linked list is sorted by check-in time
D. O(n) because it may need to traverse the linked list to find the matching check-in node

Solution

  1. Step 1: Identify linked list traversal

    The checkOut method traverses the linked list from head to find the node with matching id, which can take up to O(n) time.
  2. Step 2: Analyze route data update

    Updating the hash map for route data is O(1) average, so it does not dominate complexity.
  3. Final Answer:

    Option D -> Option D
  4. Quick Check:

    Traversal over n nodes dominates, so O(n) time [OK]
Hint: Linked list traversal dominates checkOut time [OK]
Common Mistakes:
  • Assuming O(1) because head is used directly
5. Consider the following buggy code snippet for merging two skylines. Which line contains the subtle bug that can cause incorrect skyline output?
medium
A. Line where merged.append([x, max_h]) is called unconditionally
B. Line where x, h1 = left[i] inside first if
C. Line where max_h is computed with max(h1, h2)
D. Line where i and j are incremented in the else block

Solution

  1. Step 1: Identify difference from correct code

    The bug is that merged.append is called every iteration without checking if height changed, causing redundant points.
  2. Step 2: Understand impact of redundant points

    Appending points even when max_h equals previous height leads to unnecessary key points, failing correctness checks.
  3. Final Answer:

    Option A -> Option A
  4. Quick Check:

    Correct code appends only if height changes [OK]
Hint: Appending only on height change avoids redundant points [OK]
Common Mistakes:
  • Appending every iteration regardless of height change
  • Incorrect pointer increments
Recommended

Ready to prove it?

Pass the test and this pattern moves unknown strong while earning +1 Grip toward your certificate.

10 questions~5 mineasy hard