Talentd
FleetCode
Make and follow sheets.Sign in

Design a Text Editor solution and explanation

HardDesignDoubly-Linked ListLinked ListSimulation8 min read52.1% acceptanceUpdated 9 Apr 2026
2296. Design a Text Editor (Leetcode Hard)Programming Live with Larry · 29:17 · 2K viewsWatch on YouTube
8 more videos on this problem
  1. 2296. Design a Text Editor (Leetcode Hard)Programming Live with Larry · 29:17 · 2K views
  2. Design a Text Editor(Leetcode Weekly 296)Soumya Bhattacharjee · 9:48 · 2K views
  3. Weekly Contest 296 | 2296. Design a Text EditorcodingMohan · 12:53 · 1K views
  4. Leetcode 2296 Design a Text Editor | Using Stacks | HindiThinkCode · 14:38 · 1K views
  5. Design a Text Editor - Leetcode 2296 - Doubly Linked List - PythonEffective Coder · 11:41 · 637 views
  6. Design a Text Editor - Leetcode 2296 - Two Lists - PythonEffective Coder · 15:42 · 543 views
  7. 【每日一题】LeetCode 2296. Design a Text EditorHuifeng Guan · 25:11 · 508 views
  8. Heavenislost LeetCode #2296 Design a Text EditorLei Feng · 7:49 · 206 views
  9. Two Stacks Leetcode 2296 Design a Text Editor带老婆转码 · 8:54 · 70 views

The problem

Design a text editor with a cursor that can do the following:

  • Add text to where the cursor is.
  • Delete text from where the cursor is (simulating the backspace key).
  • Move the cursor either left or right.

When deleting text, only characters to the left of the cursor will be deleted. The cursor will also remain within the actual text and cannot be moved beyond it. More formally, we have that 0 <= cursor.position <= currentText.length always holds.

Implement the TextEditor class:

  • TextEditor() Initializes the object with empty text.
  • void addText(string text) Appends text to where the cursor is. The cursor ends to the right of text.
  • int deleteText(int k) Deletes k characters to the left of the cursor. Returns the number of characters actually deleted.
  • string cursorLeft(int k) Moves the cursor to the left k times. Returns the last min(10, len) characters to the left of the cursor, where len is the number of characters to the left of the cursor.
  • string cursorRight(int k) Moves the cursor to the right k times. Returns the last min(10, len) characters to the left of the cursor, where len is the number of characters to the left of the cursor.

Example 1:

Input
["TextEditor", "addText", "deleteText", "addText", "cursorRight", "cursorLeft", "deleteText", "cursorLeft", "cursorRight"]
[[], ["leetcode"], [4], ["practice"], [3], [8], [10], [2], [6]]
Output
[null, null, 4, null, "etpractice", "leet", 4, "", "practi"]

Explanation
TextEditor textEditor = new TextEditor(); // The current text is "|". (The '|' character represents the cursor)
textEditor.addText("leetcode"); // The current text is "leetcode|".
textEditor.deleteText(4); // return 4
                          // The current text is "leet|". 
                          // 4 characters were deleted.
textEditor.addText("practice"); // The current text is "leetpractice|". 
textEditor.cursorRight(3); // return "etpractice"
                           // The current text is "leetpractice|". 
                           // The cursor cannot be moved beyond the actual text and thus did not move.
                           // "etpractice" is the last 10 characters to the left of the cursor.
textEditor.cursorLeft(8); // return "leet"
                          // The current text is "leet|practice".
                          // "leet" is the last min(10, 4) = 4 characters to the left of the cursor.
textEditor.deleteText(10); // return 4
                           // The current text is "|practice".
                           // Only 4 characters were deleted.
textEditor.cursorLeft(2); // return ""
                          // The current text is "|practice".
                          // The cursor cannot be moved beyond the actual text and thus did not move. 
                          // "" is the last min(10, 0) = 0 characters to the left of the cursor.
textEditor.cursorRight(6); // return "practi"
                           // The current text is "practi|ce".
                           // "practi" is the last min(10, 6) = 6 characters to the left of the cursor.

Constraints:

  • 1 <= text.length, k <= 40
  • text consists of lowercase English letters.
  • At most 2 * 104 calls in total will be made to addText, deleteText, cursorLeft and cursorRight.

Follow-up: Could you find a solution with time complexity of O(k) per call?

Approach overview

Problem Overview: You need to design a text editor that supports operations such as inserting text, deleting characters to the left of the cursor, and moving the cursor left or right. After cursor movement, the editor must return up to the last 10 characters to the left of the cursor. The challenge is maintaining efficient updates while simulating real editor behavior.

Approach 1: Split List Data Structure (Two Stacks Simulation) (Time: O(k) per operation, Space: O(n))

Maintain two dynamic lists representing text on the left and right of the cursor. The left list stores characters before the cursor, and the right list stores characters after it. Adding text pushes characters onto the left list. Deleting removes characters from the end of the left list. Cursor movement transfers characters between the lists: moving left pops from the left list and pushes to the right list, while moving right does the opposite. This split structure simulates cursor behavior efficiently because each character move is constant time. When returning the last 10 characters, slice the tail of the left list. This approach works well in Python or JavaScript because arrays support fast append and pop operations.

Approach 2: Node-Based Doubly Linked List (Time: O(k) per operation, Space: O(n))

Model the editor using a doubly linked list where each node represents a character. Maintain a pointer to the current cursor position. Inserting text creates new nodes and links them directly before the cursor. Deleting characters walks backward from the cursor and removes nodes while fixing the prev and next references. Cursor movement simply updates the pointer by traversing through neighboring nodes. Retrieving the last 10 characters requires iterating up to 10 nodes to the left of the cursor. This structure closely mirrors how text editors internally represent editable buffers and demonstrates strong understanding of linked list manipulation.

Recommended for interviews: The split list approach is usually preferred. It is simpler to implement, avoids pointer bugs, and clearly demonstrates how a stack-like structure can simulate cursor movement. The doubly linked list version shows deeper knowledge of data structure design and pointer manipulation, which some interviewers appreciate for system-style problems. Showing both ideas demonstrates understanding of simulation tradeoffs and editor buffer design.

Approach 1: Split List Data Structure

We model the text editor as two lists, 'left' and 'right', representing text to the left and right of the cursor respectively. This simulates efficient text editing operations by leveraging lists for append and pop operations. The cursor position is implicitly represented as the boundary between the two lists. Thus, operations like cursor movements and text manipulations can be done efficiently.

class TextEditor:
    def __init__(self):
        self.left = []  # Characters on the left of the cursor
        self.right = []  # Characters on the right of the cursor

    def addText(self, text: str) -> None:
        for char in text:
            self.left.append(char)

    def deleteText(self, k: int) -> int:
        count = 0
        while self.left and count < k:
            self.left.pop()
            count += 1
        return count

    def cursorLeft(self, k: int) -> str:
        while self.left and k > 0:
            self.right.append(self.left.pop())
            k -= 1
        return ''.join(self.left[-10:])

    def cursorRight(self, k: int) -> str:
        while self.right and k > 0:
            self.left.append(self.right.pop())
            k -= 1
        return ''.join(self.left[-10:])
class TextEditor {
    constructor() {
        this.left = [];
        this.right = [];
    }

    addText(text) {
        for (let char of text) {
            this.left.push(char);
        }
    }

    deleteText(k) {
        let count = 0;
        while (this.left.length > 0 && count < k) {
            this.left.pop();
            count++;
        }
        return count;
    }

    cursorLeft(k) {
        while (this.left.length > 0 && k > 0) {
            this.right.push(this.left.pop());
            k--;
        }
        return this.left.slice(-10).join('');
    }

    cursorRight(k) {
        while (this.right.length > 0 && k > 0) {
            this.left.push(this.right.pop());
            k--;
        }
        return this.left.slice(-10).join('');
    }
}

The implementation uses two lists, 'left' and 'right'. The 'addText' method appends characters to the 'left' list simulating the cursor being at the end of 'left'. The 'deleteText' method removes characters from the end of the 'left' list. The cursor manipulations use the 'pop' and 'append' methods to move characters between the two lists, maintaining efficient operations.

Complexity

Time Complexity: O(k) per operation, since at most k characters are processed.
Space Complexity: O(n), where n is the length of the text stored.

Try this approach in the editor

Approach 2: Nodes Based Doubly Linked List

This approach involves a custom implementation of a doubly linked list to maintain character sequences. The text is modeled using nodes where each node represents a character. The cursor is a pointer to the current node, enabling dynamic and efficient character operations akin to list insertions and deletions with cursor adjustments.

class TextEditor {
    private Node cursor;

    public TextEditor() {
        cursor = new Node('|'); // A dummy cursor character
    }

    private static class Node {
        char val;
        Node prev, next;
        Node(char val) {
            this.val = val;
        }
    }

    public void addText(String text) {
        for (char ch : text.toCharArray()) {
            Node node = new Node(ch);
            node.prev = cursor;
            node.next = cursor.next;
            if (cursor.next != null) cursor.next.prev = node;
            cursor.next = node;
            cursor = node;
        }
    }

    public int deleteText(int k) {
        int count = 0;
        while (cursor.val != '|' && count < k) {
            cursor = cursor.prev;
            cursor.next = cursor.next.next; // Remove the node after cursor
            count++;
        }
        return count;
    }

    public String cursorLeft(int k) {
        while (cursor.prev != null && cursor.val != '|' && k > 0) {
            cursor = cursor.prev;
            k--;
        }
        return getLastCharacters(10);
    }

    public String cursorRight(int k) {
        while (cursor.next != null && k > 0) {
            cursor = cursor.next;
            k--;
        }
        return getLastCharacters(10);
    }

    private String getLastCharacters(int len) {
        Node temp = cursor;
        StringBuilder result = new StringBuilder();
        while (temp.prev != null && temp.val != '|' && len > 0) {
            result.insert(0, temp.val);
            temp = temp.prev;
            len--;
        }
        return result.toString();
    }
}
#include <string>

class TextEditor {
    struct Node {
        char val;
        Node* prev;
        Node* next;
        Node(char _val) : val(_val), prev(nullptr), next(nullptr) {}
    };

    Node* cursor;

public:
    TextEditor() {
        cursor = new Node('|'); // Initialize dummy cursor as separator
    }

    void addText(const std::string& text) {
        for (char ch : text) {
            Node* node = new Node(ch);
            node->prev = cursor;
            node->next = cursor->next;
            if (cursor->next) cursor->next->prev = node;
            cursor->next = node;
            cursor = node;
        }
    }

    int deleteText(int k) {
        int count = 0;
        while (cursor->val != '|' && count < k) {
            Node* toDelete = cursor;
            cursor = cursor->prev;
            cursor->next = toDelete->next;
            delete toDelete;
            count++;
        }
        return count;
    }

    std::string cursorLeft(int k) {
        while (cursor->prev && cursor->val != '|' && k > 0) {
            cursor = cursor->prev;
            k--;
        }
        return getLastCharacters(10);
    }

    std::string cursorRight(int k) {
        while (cursor->next && k > 0) {
            cursor = cursor->next;
            k--;
        }
        return getLastCharacters(10);
    }

    std::string getLastCharacters(int len) {
        std::string result;
        Node* temp = cursor;
        while (temp->prev && temp->val != '|' && len > 0) {
            result.insert(result.begin(), temp->val);
            temp = temp->prev;
            len--;
        }
        return result;
    }
};

The Java solution implements a doubly linked list, where nodes are linked bidirectionally. This allows efficient addition, deletion, and cursor movement. The dummy cursor node acts as a guide, and moving left or right is akin to traversing the linked structure. This provides a highly flexible editor model.

Complexity

Time Complexity: O(k), k being the number of characters moving or deleting.
Space Complexity: O(n), where n is the number of characters in the text.

Try this approach in the editor

Approach 3: Left and Right Stacks

We can use two stacks, left and right, where the stack left stores the characters to the left of the cursor, and the stack right stores the characters to the right of the cursor.

  • When calling the addText method, we push the characters in text onto the left stack one by one. The time complexity is O(|text|).
  • When calling the deleteText method, we pop characters from the left stack up to k times. The time complexity is O(k).
  • When calling the cursorLeft method, we pop characters from the left stack up to k times, then push the popped characters onto the right stack one by one, and finally return up to 10 characters from the left stack. The time complexity is O(k).
  • When calling the cursorRight method, we pop characters from the right stack up to k times, then push the popped characters onto the left stack one by one, and finally return up to 10 characters from the left stack. The time complexity is O(k).
class TextEditor {
    private left: string[];
    private right: string[];

    constructor() {
        this.left = [];
        this.right = [];
    }

    addText(text: string): void {
        this.left.push(...text);
    }

    deleteText(k: number): number {
        k = Math.min(k, this.left.length);
        for (let i = 0; i < k; i++) {
            this.left.pop();
        }
        return k;
    }

    cursorLeft(k: number): string {
        k = Math.min(k, this.left.length);
        for (let i = 0; i < k; i++) {
            this.right.push(this.left.pop()!);
        }
        return this.left.slice(-10).join('');
    }

    cursorRight(k: number): string {
        k = Math.min(k, this.right.length);
        for (let i = 0; i < k; i++) {
            this.left.push(this.right.pop()!);
        }
        return this.left.slice(-10).join('');
    }
}

/**
 * Your TextEditor object will be instantiated and called as such:
 * var obj = new TextEditor()
 * obj.addText(text)
 * var param_2 = obj.deleteText(k)
 * var param_3 = obj.cursorLeft(k)
 * var param_4 = obj.cursorRight(k)
 */
type TextEditor struct {
	left, right []byte
}

func Constructor() TextEditor {
	return TextEditor{}
}

func (this *TextEditor) AddText(text string) {
	this.left = append(this.left, text...)
}

func (this *TextEditor) DeleteText(k int) int {
	k = min(k, len(this.left))
	if k < len(this.left) {
		this.left = this.left[:len(this.left)-k]
	} else {
		this.left = []byte{}
	}
	return k
}

func (this *TextEditor) CursorLeft(k int) string {
	k = min(k, len(this.left))
	for ; k > 0; k-- {
		this.right = append(this.right, this.left[len(this.left)-1])
		this.left = this.left[:len(this.left)-1]
	}
	return string(this.left[max(len(this.left)-10, 0):])
}

func (this *TextEditor) CursorRight(k int) string {
	k = min(k, len(this.right))
	for ; k > 0; k-- {
		this.left = append(this.left, this.right[len(this.right)-1])
		this.right = this.right[:len(this.right)-1]
	}
	return string(this.left[max(len(this.left)-10, 0):])
}

/**
 * Your TextEditor object will be instantiated and called as such:
 * obj := Constructor();
 * obj.AddText(text);
 * param_2 := obj.DeleteText(k);
 * param_3 := obj.CursorLeft(k);
 * param_4 := obj.CursorRight(k);
 */
struct TextEditor {
    left: String,
    right: String,
}

impl TextEditor {
    fn new() -> Self {
        TextEditor {
            left: String::new(),
            right: String::new(),
        }
    }

    fn add_text(&mut self, text: String) {
        self.left.push_str(&text);
    }

    fn delete_text(&mut self, k: i32) -> i32 {
        let k = k.min(self.left.len() as i32) as usize;
        self.left.truncate(self.left.len() - k);
        k as i32
    }

    fn cursor_left(&mut self, k: i32) -> String {
        let k = k.min(self.left.len() as i32) as usize;
        for _ in 0..k {
            if let Some(c) = self.left.pop() {
                self.right.push(c);
            }
        }
        self.get_last_10_chars()
    }

    fn cursor_right(&mut self, k: i32) -> String {
        let k = k.min(self.right.len() as i32) as usize;
        for _ in 0..k {
            if let Some(c) = self.right.pop() {
                self.left.push(c);
            }
        }
        self.get_last_10_chars()
    }

    fn get_last_10_chars(&self) -> String {
        let len = self.left.len();
        self.left[len.saturating_sub(10)..].to_string()
    }
}
Try this approach in the editor

Complexity comparison

ApproachComplexity
Split List Data Structure

Time Complexity: O(k) per operation, since at most k characters are processed.
Space Complexity: O(n), where n is the length of the text stored.

Nodes Based Doubly Linked List

Time Complexity: O(k), k being the number of characters moving or deleting.
Space Complexity: O(n), where n is the number of characters in the text.

Left and Right Stacks–

Complexity analysis

ApproachTimeSpaceWhen to Use
Split List (Two Stacks)O(k) per operationO(n)Preferred for Python/JavaScript implementations where array push/pop are efficient
Node-Based Doubly Linked ListO(k) per operationO(n)Useful when practicing pointer manipulation or implementing editor buffers

Frequently asked questions

Is Design a Text Editor easy or hard?

Design a Text Editor is classified as a Hard problem because it combines multiple operations, cursor simulation, and careful data structure design. The logic is straightforward once the correct structure—two stacks or a doubly linked list—is chosen.

Design a Text Editor Python/Java solution

Python solutions usually implement the split list technique using two lists for left and right cursor text. Java solutions often use a custom doubly linked list where nodes represent characters and a cursor pointer controls insertion and deletion operations.

How to solve Design a Text Editor in O(n)?

Use a split buffer strategy with two lists or stacks representing text before and after the cursor. Insertions append to the left list, deletions pop from it, and cursor movement transfers characters between the two lists. Across all operations the total processed characters remain linear relative to the input size, giving efficient O(n) overall behavior.

What is the best approach for Design a Text Editor?

The split list (two stacks) approach is typically the best solution. It stores characters left and right of the cursor in separate lists, making insertions, deletions, and cursor movement efficient. Each operation runs in O(k) time where k is the number of characters moved or inserted, and the total space complexity is O(n).

Is Design a Text Editor asked at Google/Amazon/Meta?

Design-style data structure problems like this appear in interviews at large tech companies including Google, Amazon, and Meta. They test understanding of linked lists, stacks, and simulation of real systems such as text editors or buffers.

What data structure is used in Design a Text Editor?

Common implementations use two stacks (or dynamic arrays) to represent the text around the cursor. Another approach uses a doubly linked list with a cursor pointer so characters can be inserted and removed efficiently in the middle of the text.

What is the time complexity of Design a Text Editor?

Most implementations achieve O(k) time per operation, where k is the number of characters affected by the operation. For example, adding text of length k or moving the cursor by k positions requires O(k) work. Space complexity is O(n) because all characters in the editor must be stored.

Comments

Now write it yourselfClose this page and solve Design a Text Editor in the editor, without looking.
Practise Design a Text Editor