githubEdit

20. Implement Undo & Redo

βœ… GFG solution to the Implement Undo & Redo problem: build a text editor with append, undo, redo, and read operations using stack-based approach. πŸš€

The problem can be found at the following link: πŸ”— Question Linkarrow-up-right

🧩 Problem Description

You are given a text document that is initially empty. You need to implement the following operations:

  • append(char x) - Append the character x to the end of the document.

  • undo() - Undo the most recent append operation (remove the last appended character).

  • redo() - Reapply the most recent undone operation (restore the last character removed by undo).

  • read() - Return the current content of the document as a string.

The operations are represented in numeric form through queries:

  • 1 x - Call append(x)

  • 2 - Call undo()

  • 3 - Call redo()

  • 4 - Call read()

πŸ“˜ Examples

Example 1

Input: arr[] = [[1 'A'], [1 'B'], [1 'C'], [2], [4], [3], [4]]
Output: ["AB", "ABC"]
Explanation: 
1st query: Append('A'), Document contains "A".
2nd query: Append('B'), Document contains "AB".
3rd query: Append('C'), Document contains "ABC".
4th query: UNDO(), Last character is removed, Document contains "AB".
5th query: READ(), Document content "AB" will be printed.
6th query: REDO(), Document contains "ABC".
7th query: READ(), Document content "ABC" will be printed.

Example 2

πŸ”’ Constraints

  • $1 \le q \le 10^4$

βœ… My Approach

The optimal approach uses a Stack-Based technique to efficiently manage undo and redo operations:

Stack-Based Approach

  1. Data Structures:

    • Use a string d to maintain the current document content.

    • Use a stack r to store characters that were undone for redo functionality.

  2. Append Operation:

    • Add the character to the end of the document string.

    • Clear the redo stack since any new append invalidates previous redo history.

  3. Undo Operation:

    • If the document is not empty, remove the last character from the document.

    • Push this removed character onto the redo stack for potential redo operation.

  4. Redo Operation:

    • If the redo stack is not empty, pop the top character.

    • Append this character back to the document string.

  5. Read Operation:

    • Simply return the current document string.

πŸ“ Time and Auxiliary Space Complexity

  • Expected Time Complexity: O(1) for append, undo, and redo operations, as each operation performs a constant number of stack/string operations. O(n) for read operation where n is the length of the document, as we need to return the entire string.

  • Expected Auxiliary Space Complexity: O(n), where n is the total number of characters that have been appended to the document. In the worst case, all appended characters could be stored in the redo stack.

πŸ§‘β€πŸ’» Code (C++)

chevron-right⚑ View Alternative Approaches with Code and Analysishashtag

πŸ“Š 2️⃣ Deque-Based History Approach

πŸ’‘ Algorithm Steps:

  1. Use a deque to maintain the document state for efficient operations.

  2. Store snapshots of document states in a vector for undo capability.

  3. Track current position in history to enable both undo and redo.

  4. Navigate through document history using position pointer.

πŸ“ Complexity Analysis:

  • Time: ⏱️ O(n) per operation - Due to state copying

  • Auxiliary Space: πŸ’Ύ O(n*m) - Stores m versions of document with n characters

βœ… Why This Approach?

  • Full history preservation for unlimited undo/redo.

  • Flexible navigation through document states.

  • Supports complex multi-operation undo scenarios.

πŸ“Š 3️⃣ Two-Stack Command Pattern

πŸ’‘ Algorithm Steps:

  1. Maintain two stacks: one for undo commands and one for redo commands.

  2. Store operation type and character in each stack entry.

  3. On append, push to undo stack and clear redo stack.

  4. Execute reverse operations when undo/redo is triggered.

πŸ“ Complexity Analysis:

  • Time: ⏱️ O(1) - Constant time per operation

  • Auxiliary Space: πŸ’Ύ O(n) - Stores operation history

βœ… Why This Approach?

  • Extensible to support multiple operation types.

  • Clean command pattern implementation.

  • Efficient operation tracking and reversal.

πŸ“Š 4️⃣ Vector with Position Tracking

πŸ’‘ Algorithm Steps:

  1. Use vector to store all characters ever appended to document.

  2. Track active length of document instead of physically removing characters.

  3. Adjust length pointer for undo and redo operations.

  4. Return substring based on current active length for read operation.

πŸ“ Complexity Analysis:

  • Time: ⏱️ O(1) for append/undo/redo, O(n) for read

  • Auxiliary Space: πŸ’Ύ O(n) - Stores maximum document size

βœ… Why This Approach?

  • Minimal memory reallocation with vector.

  • Fast pointer-based undo/redo operations.

  • Cache-friendly sequential memory access.

πŸ“Š 5️⃣ List-Based Approach

πŸ’‘ Algorithm Steps:

  1. Use a doubly linked list or list container to store characters.

  2. Maintain iterators for current document end and redo position.

  3. On append, insert character and move iterator forward.

  4. On undo, move iterator backward; on redo, move forward.

πŸ“ Complexity Analysis:

  • Time: ⏱️ O(1) for append/undo/redo, O(n) for read

  • Auxiliary Space: πŸ’Ύ O(n)

βœ… Why This Approach?

  • Natural implementation with list iterators.

  • Efficient insertion and deletion at any position.

  • Clean separation between current and future states.

πŸ†š πŸ” Comparison of Approaches

πŸš€ Approach

⏱️ Time Complexity

πŸ’Ύ Space Complexity

βœ… Pros

⚠️ Cons

🏷️ Stack-Based

🟒 O(1)

🟒 O(n)

πŸš€ Optimal & simple

πŸ”§ Single character operations only

πŸ” Deque History

🟑 O(n)

πŸ”΄ O(n*m)

πŸ“– Full history access

πŸ’Ύ High memory usage

πŸ“Š Command Pattern

🟒 O(1)

🟒 O(n)

🎯 Extensible design

🐌 Extra overhead for simple cases

πŸ”„ Vector Tracking

🟒 O(1)

🟒 O(n)

⭐ Cache efficient

πŸ”§ Read operation slower

πŸ“ List-Based

🟒 O(1)

🟒 O(n)

🎯 Iterator elegance

🐌 Not cache-friendly

πŸ† Best Choice Recommendation

🎯 Scenario

πŸŽ–οΈ Recommended Approach

πŸ”₯ Performance Rating

πŸ… Simple editor operations

πŸ₯‡ Stack-Based

β˜…β˜…β˜…β˜…β˜…

πŸ“– Complex undo history needed

πŸ₯ˆ Deque History

β˜…β˜…β˜…β˜…β˜†

πŸ”§ Multiple operation types

πŸ₯‰ Command Pattern

β˜…β˜…β˜…β˜…β˜†

🎯 Memory optimization

πŸ… Vector Tracking

β˜…β˜…β˜…β˜…β˜†

πŸ“š Iterator-based design

πŸŽ–οΈ List-Based

β˜…β˜…β˜…β˜†β˜†

β˜• Code (Java)

🐍 Code (Python)

🧠 Contribution and Support

For discussions, questions, or doubts related to this solution, feel free to connect on LinkedIn: πŸ“¬ Any Questions?arrow-up-right. Let's make this learning journey more collaborative!

⭐ If you find this helpful, please give this repository a star! ⭐


πŸ“Visitor Count

Visitor counter

Last updated