[LeetCode] 每日一题 2296. 设计一个文本编辑器
题目链接
题目描述
请你设计一个带光标的文本编辑器,它可以实现以下功能:
添加:在光标所在处添加文本。
删除:在光标所在处删除文本(模拟键盘的删除键)。
移动:将光标往左或者往右移动。
当删除文本时,只有光标左边的字符会被删除。光标会留在文本内,也就是说任意时候 0 <= cursor.position <= currentText.length
都成立。
请你实现 TextEditor
类:
TextEditor()
用空文本初始化对象。void addText(string text)
将text
添加到光标所在位置。添加完后光标在text
的右边。int deleteText(int k)
删除光标左边k
个字符。返回实际删除的字符数目。string cursorLeft(int k)
将光标向左移动k
次。返回移动后光标左边min(10, len)
个字符,其中len
是光标左边的字符数目。string cursorRight(int k)
将光标向右移动k
次。返回移动后光标左边min(10, len)
个字符,其中len
是光标左边的字符数目。
示例输入
示例
输入:
["TextEditor", "addText", "deleteText", "addText", "cursorRight", "cursorLeft", "deleteText", "cursorLeft", "cursorRight"]
[[], ["leetcode"], [4], ["practice"], [3], [8], [10], [2], [6]]
输出:
[null, null, 4, null, "etpractice", "leet", 4, "", "practi"]
解释:
TextEditor textEditor = new TextEditor(); // 当前 text 为 "|" 。('|' 字符表示光标)
textEditor.addText("leetcode"); // 当前文本为 "leetcode|" 。
textEditor.deleteText(4); // 返回 4
// 当前文本为 "leet|" 。
// 删除了 4 个字符。
textEditor.addText("practice"); // 当前文本为 "leetpractice|" 。
textEditor.cursorRight(3); // 返回 "etpractice"
// 当前文本为 "leetpractice|".
// 光标无法移动到文本以外,所以无法移动。
// "etpractice" 是光标左边的 10 个字符。
textEditor.cursorLeft(8); // 返回 "leet"
// 当前文本为 "leet|practice" 。
// "leet" 是光标左边的 min(10, 4) = 4 个字符。
textEditor.deleteText(10); // 返回 4
// 当前文本为 "|practice" 。
// 只有 4 个字符被删除了。
textEditor.cursorLeft(2); // 返回 ""
// 当前文本为 "|practice" 。
// 光标无法移动到文本以外,所以无法移动。
// "" 是光标左边的 min(10, 0) = 0 个字符。
textEditor.cursorRight(6); // 返回 "practi"
// 当前文本为 "practi|ce" 。
// "practi" 是光标左边的 min(10, 6) = 6 个字符。
提示
1 <= text.length, k <= 40
text
只含有小写英文字母。调用
addText
,deleteText
,cursorLeft
和cursorRight
的 总 次数不超过2 * 10^4
次。
进阶:你能设计并实现一个每次调用时间复杂度为 O(k)
的解决方案吗?
解题思路
在本题中,我们需要设计一个文本编辑器,支持添加、删除文本以及移动光标的操作。考虑到光标的移动和文本编辑的需求,我决定使用双向链表来存储文本。每个字符都作为链表中的一个节点,这样不仅可以方便地添加或删除字符,还能高效地移动光标
为了方便操作,我在一开始构建了一个虚拟的头结点,这样就可以统一处理光标的移动和文本的增删,不需要特别判断链表的边界条件。每次添加文本时,我就在当前光标位置插入新的节点,光标移到新增字符的右边;每次删除时,我就从当前光标左边删除指定数量的字符。对于光标的左右移动,我则通过更新当前节点的指针来实现
代码实现
class TextEditor {
static class Node {
char val;
Node next;
Node pre;
public Node(char c) {
this.val = c;
this.pre = null;
this.next = null;
}
}
Node cur;
public TextEditor() {
cur = new Node(' ');
}
public void addText(String text) {
for (char c : text.toCharArray()) {
Node temp = new Node(c);
if (cur.next != null) {
cur.next.pre = temp;
}
temp.next = cur.next;
temp.pre = cur;
cur.next = temp;
cur = cur.next;
}
}
public int deleteText(int k) {
int count = 0;
while (k-- > 0 && cur.pre != null) {
count++;
cur = cur.pre;
if (cur.next != null) {
cur.next = cur.next.next;
}
if (cur.next != null) {
cur.next.pre = cur;
}
}
return count;
}
public String cursorLeft(int k) {
while (k-- > 0 && cur.pre != null) {
cur = cur.pre;
}
return getLeftTen();
}
public String cursorRight(int k) {
while (k-- > 0 && cur.next != null) {
cur = cur.next;
}
return getLeftTen();
}
private String getLeftTen() {
StringBuilder sb = new StringBuilder();
Node temp = cur;
int count = 10;
while (count-- > 0 && temp.pre != null) {
sb.append(temp.val);
temp = temp.pre;
}
return sb.reverse().toString();
}
}
复杂度分析
时间复杂度:
addText
方法的时间复杂度是 O(m),其中 m 是添加文本的长度。我们需要遍历整个字符串并依次插入节点deleteText
方法的时间复杂度是 O(k),其中 k 是要删除的字符数。每次删除操作仅需要调整几个指针,时间复杂度是常数级别的cursorLeft
和cursorRight
方法的时间复杂度都是 O(k),其中 k 是光标需要移动的步数。每次移动光标都只需要调整当前节点的指针,复杂度为 O(k)getLeftTen
方法的时间复杂度是 O(1),因为最多需要从当前光标向左遍历 10 个字符
总结
本题设计了一个支持光标操作的文本编辑器。通过使用双向链表来存储文本,不仅能方便地在光标所在位置添加和删除字符,还能高效地处理光标的左右移动操作。在实现中,使用虚拟头结点来统一处理光标的位置,避免了边界条件的特殊处理。这个设计思路简单且高效,适合用于需要频繁修改和查询文本的场景
希望这篇分享能为你带来启发!如果你有任何问题或建议,欢迎在评论区留言,与我共同交流探讨。