题目重点:实现光标插入、向左删除和左右移动,区分单点编辑与随机跳转。
题意与假设
这道练习题保留了面经中值得训练的核心约束,并将需要澄清的条件单独列出。现场拿到变体时,先和面试官确认这些条件。
- 只明确要求写text editor,没有操作API。练习定义add_text、delete_left、move_left/right、text和cursor;不声称这是给定API。按Unicode码点移动,不保证字素/显示列。
- 单用户内存编辑器,插入发生在光标,删除左侧最多K字符;不实现undo、多光标或协作一致性。
解题路线
- left按正常顺序保存光标左侧,right反向保存右侧。逻辑文本始终为left+reverse(right),光标位置len(left);移动只是两个栈间迁移元素。
- 向左移时left.pop推right,向右则反向操作。每次迁移保留逻辑文本、不改变字符顺序;遇边界只移可用数量。插入文本只扩展left,删除只移除left尾段。
- 普通数组在中间插入要搬尾部;两栈让光标附近操作只依赖编辑量。若大量随机跳转或查询大范围内容,rope/piece table可能更适合,并需讨论索引和碎片合并。
- text()会重新生成完整字符串,不能称查询O(1)。Undo要记录位置/删除文本/插入内容与事务边界;不能仅把光标历史当可恢复全部编辑。
手动走一遍
输入abc,光标末尾;向左1次后left=ab、right=c,插入XY得到abXYc,光标4;删除左侧2恢复abc,光标2;向右超长K停在末尾。
Python 参考实现
下面的代码按上文假设实现,适合本地运行并追加自己的测试用例。
代码区可横向滚动;使用键盘时,先聚焦代码区再按方向键查看。
class TextEditor:
"""Training API: codepoint cursor, left deletion, no undo/grapheme promises."""
def __init__(self):
self.left=[];self.right=[]
def add_text(self,text):
self.left.extend(text)
def delete_left(self,k):
if k<0:raise ValueError('nonnegative k')
count=min(k,len(self.left))
if count:del self.left[-count:]
return count
def move_left(self,k):
if k<0:raise ValueError('nonnegative k')
for _ in range(min(k,len(self.left))):self.right.append(self.left.pop())
def move_right(self,k):
if k<0:raise ValueError('nonnegative k')
for _ in range(min(k,len(self.right))):self.left.append(self.right.pop())
def text(self):
return ''.join(self.left)+''.join(reversed(self.right))
@property
def cursor(self):
return len(self.left)
复杂度
插入M字符O(M)摊还,移动K需O(min(K,可移数量)),删除D字符O(D)含释放;text()时间/输出O(N)。存储O(N),Python栈的扩容摊还成本另计,单次大扩容可线性。
易错点与追问
容易失分的边界
- right存储方向弄反,移动后颠倒文本。
- k=0使用[-0:]错误删全数组。
- 把码点光标等同于用户看到的字符或字形。
继续深挖
- 怎样把一串编辑合成一个undo动作?
- 协作编辑如何引入稳定位置身份及OT/CRDT,不把两栈当分布式协议?
