Algorithm 用于编辑文本的红黑树
我在读华金·昆卡·阿贝拉的作品。他谈到使用红黑树实现一个工件表,而不是一个双链表 我很难理解这与正在进行更改的缓冲区之间的关系。例如,以这两个缓冲区(原始、附加)为例: 假设工件表是这样的:Algorithm 用于编辑文本的红黑树,algorithm,binary-tree,text-editor,red-black-tree,Algorithm,Binary Tree,Text Editor,Red Black Tree,我在读华金·昆卡·阿贝拉的作品。他谈到使用红黑树实现一个工件表,而不是一个双链表 我很难理解这与正在进行更改的缓冲区之间的关系。例如,以这两个缓冲区(原始、附加)为例: 假设工件表是这样的: buffer start length original 0 2 original 5 2 append 0 1 ------------------ ------------------- ---------------- Buff
buffer start length
original 0 2
original 5 2
append 0 1
------------------ ------------------- ----------------
Buffer = ORIGINAL| |Buffer = ORIGINAL| |Buffer = APPEND
Start = 0 |<--|Start = 5 |<--|Start = 0
Length = 2 | |Length = 2 | |Length = 1
Next = 0x01 |-->|Next = 0x02 |-->|Next = NULL
Previous = NULL | |Previous = 0x01 | |Previous = 0x01
------------------ ------------------- ----------------
------------------
size = 2
size_left = 1
size_right = 2
colour = black
------------------
/ \
/ \
/ \
---------------- ----------------
size = 1 size = 2
size_left = 0 size_left = 0
size_right = 0 size_right = 0
colour = red colour = red
---------------- ----------------
/ \ / \
/ \ / \
NULL NULL NULL NULL
我们应该以以下方式结束:
Hey!\0
使用双链接列表,可以实现如下方式:
buffer start length
original 0 2
original 5 2
append 0 1
------------------ ------------------- ----------------
Buffer = ORIGINAL| |Buffer = ORIGINAL| |Buffer = APPEND
Start = 0 |<--|Start = 5 |<--|Start = 0
Length = 2 | |Length = 2 | |Length = 1
Next = 0x01 |-->|Next = 0x02 |-->|Next = NULL
Previous = NULL | |Previous = 0x01 | |Previous = 0x01
------------------ ------------------- ----------------
------------------
size = 2
size_left = 1
size_right = 2
colour = black
------------------
/ \
/ \
/ \
---------------- ----------------
size = 1 size = 2
size_left = 0 size_left = 0
size_right = 0 size_right = 0
colour = red colour = red
---------------- ----------------
/ \ / \
/ \ / \
NULL NULL NULL NULL
我看不到在编辑后重新绘制文档其余部分的清晰方法。当然,插入/删除/查找将更快地将片段添加到树中。但我不知道如何构造编辑的缓冲区以供查看
我错过了什么?如果我有一个编辑器,并且我删除/插入了一段文本,我将如何遍历树以重新绘制缓冲区并正确反映此编辑?而且,这怎么会比链表提供的O(n)时间复杂度快呢?我真的不理解您提供的树图,因为(与链表图不同)它们似乎与实际存储的数据没有关系。实际上,它们将具有基本相同的数据字段(Buffer、Start和Length),再加上一个Size,即节点所在子树中片段的总大小。它们将有左指针和右指针(子指针),而不是上一个和下一个指针 当然,为了保持树的平衡,它们需要一些额外的数据(在红/黑树的情况下是一个红/黑位,但我认为保持平衡的机制在这里并不重要;例如,可以使用AVL树而不是红/黑树。因此,我将忽略节点的这一部分 Size字段对于查找给定偏移量处的数据是必需的(因此,如果不需要进行此类查找,则可以省略该字段)。我认为链接文章以片段度量大小,而我倾向于以字符(甚至字节)度量大小,这就是我在这里要说明的。正如链接文章所指出的,Size字段可以在对数时间内轻松维护,因为它指的是子树的大小,而不是它在数据流中的位置 使用“大小”字段按缓冲区偏移量查找节点。如果偏移量小于左子级的大小,则递归到左子级;如果至少是当前长度加上左子级的大小,则从偏移量中减去该和,然后递归到右子级。否则,当前节点包含所需的偏移量。This所需的时间不能超过最大树深度,如果树处于合理平衡状态,则最大树深度为O(对数N) 我对您的链表图也有点困惑,在我看来,它似乎代表缓冲区
He |!\0 | y
,而我希望它是He | y |!\0
:
------------------ ------------------- -------------------
Buffer = ORIGINAL| |Buffer = APPEND | |Buffer = ORIGINAL|
Start = 0 |<--|Start = 0 |<--|Start = 5 |
Length = 2 | |Length = 1 | |Length = 2 |
Next = 0x01 |-->|Next = 0x02 |-->|Next = NULL |
Previous = NULL | |Previous = 0x01 | |Previous = 0x01 |
------------------ ------------------- -------------------
从给定节点按顺序查找下一个节点的算法如下:
如果以字符度量大小,则可以避免使用“长度”字段,因为节点直接引用的数据的长度只是节点子树大小与其子子树大小之和之间的差值将节点的大小减小为链表节点的大小,假设您可以找到某种方法将红色/黑色位(或其他平衡信息)编码为填充位 另一方面,使用父指针和两个子指针的二叉树实现有点常见(通过查看上面的遍历算法,可以清楚地看出这有什么帮助)但是,不需要存储父指针,因为它们可以在树中按深度索引的父指针数组中的树的任何给定遍历期间进行维护。此数组显然不大于最大树深度,因此可以使用一个小的(~50)定长数组 这些优化也远远超出了这个答案 如果我有一个编辑器,并且我删除/插入了一段文本,我将如何遍历树以重新绘制缓冲区并正确反映此编辑?这将如何比链表提供的O(n)时间复杂度更快 假设工件表很大,重新绘制屏幕上可见的缓冲区部分通常只需要访问几个连续节点。 假设在一个特定的编辑之后需要访问的节点位于文档的中间或接近尾端。 对于双链接列表,您可能需要遍历开始表中的多个节点才能到达编辑的开始。这就是O(n)。从那里,您可以遍历接下来的几个节点来进行绘制 使用平衡树,您可以找到O(log_2n)中的第一个节点。从那里,您可以进行顺序遍历,以访问绘制所需的下几个节点 在添加、删除或修改工件后,更新树中的位置只需从祖先的位置添加/减去一个值即可