Implement Batched Undo/Redo Layer
Company: Google
Role: Software Engineer
Category: Coding & Algorithms
Difficulty: easy
Interview Round: Technical Screen
Quick Answer: This question evaluates understanding of state management, undo/redo semantics, batched transactional edits, and memory-efficient change history representation for map-based document layers.
Part 1: Implement Batched Undo/Redo for Document Properties
Constraints
- 0 <= len(operations) <= 200000
- The total number of ('set', key, value) operations is at most 200000
- Keys and values are strings of length 1 to 50
- Batches are not nested
- If a batch contains no set operations, committing it does not create history
Examples
Input: [('begin',), ('set', 'color', 'red'), ('end',), ('snapshot',), ('undo',), ('snapshot',), ('redo',), ('snapshot',)]
Expected Output: [{'color': 'red'}, {}, {'color': 'red'}]
Explanation: A single batch is committed, undone, and redone.
Input: [('begin',), ('set', 'a', '1'), ('end',), ('begin',), ('set', 'a', '2'), ('set', 'a', '3'), ('set', 'b', '9'), ('end',), ('snapshot',), ('undo',), ('snapshot',), ('redo',), ('snapshot',)]
Expected Output: [{'a': '3', 'b': '9'}, {'a': '1'}, {'a': '3', 'b': '9'}]
Explanation: The second batch updates 'a' twice and adds 'b'. Undo must restore the exact pre-batch state.
Input: [('begin',), ('set', 'x', '1'), ('end',), ('begin',), ('set', 'y', '2'), ('end',), ('undo',), ('begin',), ('set', 'z', '3'), ('end',), ('redo',), ('snapshot',)]
Expected Output: [{'x': '1', 'z': '3'}]
Explanation: After undoing the batch that added 'y', committing a new batch clears redo history, so redo does nothing.
Input: [('undo',), ('redo',), ('snapshot',)]
Expected Output: [{}]
Explanation: Edge case: undo and redo on empty history are no-ops.
Input: [('begin',), ('set', 'a', '1'), ('snapshot',), ('set', 'a', '2'), ('snapshot',), ('end',), ('undo',), ('snapshot',)]
Expected Output: [{'a': '1'}, {'a': '2'}, {}]
Explanation: Snapshots inside an open batch should reflect live edits, but undo still reverts the whole committed batch.
Hints
- For each key touched in the current batch, record its old value only the first time that key is edited in the batch.
- Use two stacks: one for committed batches that can be undone, and one for undone batches that can be redone. Clear the redo stack when a new non-empty batch is committed.
Part 2: Compact a Batch into Memory-Efficient Change Records
Constraints
- 0 <= len(initial_document) <= 200000
- 0 <= len(edits) <= 200000
- Keys and values are strings of length 1 to 50
- Each edit is a set operation only
- The output order of 'changes' must follow the first time each key appears in edits
Examples
Input: ({'a': '1', 'b': '2'}, [('a', '3'), ('a', '4'), ('c', '5')])
Expected Output: {'changes': [('a', '4', '1', True), ('c', '5', None, False)], 'after_apply': {'a': '4', 'b': '2', 'c': '5'}, 'after_undo': {'a': '1', 'b': '2'}, 'after_redo': {'a': '4', 'b': '2', 'c': '5'}}
Explanation: Repeated edits to 'a' collapse into one record that keeps the original old value and the final new value.
Input: ({}, [('x', '1'), ('x', '2'), ('x', '3')])
Expected Output: {'changes': [('x', '3', None, False)], 'after_apply': {'x': '3'}, 'after_undo': {}, 'after_redo': {'x': '3'}}
Explanation: Edge case: a new key is written multiple times in an otherwise empty document.
Input: ({'k': 'v'}, [])
Expected Output: {'changes': [], 'after_apply': {'k': 'v'}, 'after_undo': {'k': 'v'}, 'after_redo': {'k': 'v'}}
Explanation: Edge case: an empty batch produces no compact records and changes nothing.
Input: ({'p': '7', 'q': '8'}, [('r', '1'), ('p', '9'), ('r', '2'), ('q', '0'), ('p', '10')])
Expected Output: {'changes': [('r', '2', None, False), ('p', '10', '7', True), ('q', '0', '8', True)], 'after_apply': {'p': '10', 'q': '0', 'r': '2'}, 'after_undo': {'p': '7', 'q': '8'}, 'after_redo': {'p': '10', 'q': '0', 'r': '2'}}
Explanation: The compact record list follows first appearance order: r, p, q.
Hints
- When a key appears for the first time in the batch, capture its old value from the pre-batch document, not from any earlier edit in the same batch.
- For later edits to the same key, update only the final value in its compact record.