String 如何劈开一棵绳索树?

String 如何劈开一棵绳索树?,string,algorithm,data-structures,binary-tree,String,Algorithm,Data Structures,Binary Tree,我遇到了一个绳索树作为字符串的替代数据结构 做concat很容易,但我被拆分操作卡住了。维基百科的文章说: 例如,要将图2.3中所示的22个字符的绳索拆分为两条长度为11的相等组件绳索,请查询第12个字符以在底部找到节点K。移除K和G的右子级之间的链接。转到父级G并从G的权重中减去K的权重。沿树向上移动并移除任何右链接,从这些节点(在这种情况下,仅节点D)中减去K的权重。最后,通过将新孤立节点K和H连接在一起并创建一个新的父节点P,其权重等于左侧节点K的长度,从而构建新孤立节点K和H 定位角色

我遇到了一个绳索树作为字符串的替代数据结构

做concat很容易,但我被拆分操作卡住了。维基百科的文章说:

例如,要将图2.3中所示的22个字符的绳索拆分为两条长度为11的相等组件绳索,请查询第12个字符以在底部找到节点K。移除K和G的右子级之间的链接。转到父级G并从G的权重中减去K的权重。沿树向上移动并移除任何右链接,从这些节点(在这种情况下,仅节点D)中减去K的权重。最后,通过将新孤立节点K和H连接在一起并创建一个新的父节点P,其权重等于左侧节点K的长度,从而构建新孤立节点K和H

定位角色并重新组合孤立项不是问题。但是我不理解“沿着树往上走,删除任何正确的链接,从这些节点中减去K的权重”。该示例在D处停止,但如果您一字不差地遵循这些说明,您将继续执行B并删除D。此算法中的正确停止要求是什么?如何避免只有一个子节点(左或右)的节点


解释这一部分的伪代码算法将大有帮助。

维基百科的文章不是很明确。如果当前节点是
X
,其父节点是
Y
,则只有当
X
Y
的左子节点时,才会向上移动。从视觉上看,你将尽可能向上向右移动。

经过一些修补和考虑,我认为规则应该是这样的:

首先确定向上移动的起点。

a)如果您在节点(节点A)的中间结束,则在右字符索引上拆分字符串并创建左和右节点。这些新节点的父节点是节点A。左侧节点是您的起点。向上移动时,右侧节点将添加到孤立节点

B) 如果结束于节点的开头(按字符排列),且该节点是右侧节点:拆分该节点(=>孤立节点),并使用父节点作为起点

C) 如果结束于节点的开头(按字符),而该节点是左侧节点: 拆分此节点(=>孤立节点),并将此节点用作起点

D) 如果结束于节点的末尾(按字符),且该节点是右侧节点: 使用父节点作为起点

D) 如果结束于节点的末尾(按字符),且该节点是左侧节点: 使用此节点作为起点

旅行期间: 如果节点是左侧节点:向上移动并将其右侧同级节点添加到孤立节点列表中

如果节点是右节点:向上移动(到父节点a),但不处理左同级节点。(或者,由于到目前为止所有正确的节点都是孤立的,因此可以将起始点设置为父节点A的正确节点。父节点A将是新的起始点。这样可以避免一组节点只有一个子节点。)

之后 将所有累积的孤立项合并到一个新节点中。这是新根节点的正确部分。左侧部分是旅行序列的终点


如果我错了,请纠正我。

我在下面给出了Ruby代码。它尽可能接近可执行伪代码。如果Ruby不适合您的实现,您可以始终将其用作原型。如果您不喜欢递归,可以很容易地使用标准转换使其通过显式堆栈进行迭代

split
的自然实现是递归的。有趣的例子在代码的底部

class Rope

  # Cat two ropes by building a new binary node.
  # The parent count is the left child's length.
  def cat(s)
    if self.len == 0
      s
    elsif s.len == 0
      self
    else
      Node.new(self, s, len)
    end
  end

  # Insert a new string into a rope by splitting it
  # and concatenating twice with a new leaf in the middle.
  def insert(s, p)
    a, b = split_at(p)
    a.cat(Leaf.new(s)).cat(b)
  end

end

class Leaf < Rope
  # A leaf holds characters as a string.
  attr_accessor :chars

  # Construct a new leaf with given characters.
  def initialize(chars)
    @chars = chars
  end

  # The count in this rope is just the number of characters.
  def count
    chars.length
  end

  # The length is kind of obvious.
  def len
    chars.length
  end

  # Convert to a string by just returning the characters.
  def to_s
    chars
  end

  # Split by dividing the string.
  def split_at(p)
    [ Leaf.new(chars[0...p]), Leaf.new(chars[p..-1]) ] 
  end
end

class Node < Rope
  # Fields of the binary node.
  attr_accessor :left, :right, :count

  # Construct a new binary node.
  def initialize(left, right, count)
    @left = left
    @right = right
    @count = count
  end

  # Length is our count plus right subtree length. Memoize for efficiency.
  def len
    @len ||= count + right.len
  end

  # The string rep is just concatenating the children's string reps.
  def to_s
    left.to_s + right.to_s
  end

  # Split recursively by splitting the left or right 
  # subtree and recombining the results.
  def split_at(p)
    if p < count
      a, b = left.split_at(p)
      [ a, b.cat(right) ]
    elsif p > count 
      a, b = right.split_at(p - count)
      [ left.cat(a), b ]
    else
      [ left, right ]
    end
  end
end
类绳
#通过构建一个新的二进制节点,Cat two rope。
#父项计数是左侧子项的长度。
def类别(s)
如果self.len==0
s
elsif s.len==0
自己
其他的
Node.new(self、s、len)
结束
结束
#把一根新绳子劈开,插入绳子
在中间用新叶连接两次。
def插件(s、p)
a、 b=在(p)处的拆分
a、 类别(新叶)(s)类别(b)
结束
结束
类叶<绳
#叶子将字符作为字符串保存。
属性存取器:chars
#用给定的字符构造一个新叶。
def初始化(字符)
@chars=chars
结束
#这个绳子中的计数就是字符数。
def计数
字符长度
结束
#长度有点明显。
德夫伦
字符长度
结束
#只需返回字符即可转换为字符串。
def至美国
查尔斯
结束
#通过分割字符串来分割。
在(p)处的def分离单元
[Leaf.new(chars[0…p]),Leaf.new(chars[p..-1])]
结束
结束
类节点<绳
#二进制节点的字段。
属性访问器:左、右、计数
#构造一个新的二进制节点。
def初始化(左、右、计数)
@左=左
@右=右
@计数=计数
结束
#长度是我们的计数加上右子树长度。为提高效率而备忘。
德夫伦
@len | |=计数+右。len
结束
#字符串代表只是连接孩子们的字符串代表。
def至美国
左。至右+右。至右
结束
#通过向左或向右拆分来递归拆分
#子树并重新组合结果。
在(p)处的def分离单元
如果p<0.01,则计数
a、 b=左侧。在(p)处拆分
[a,b.cat(右)]
elsif p>count
a、 b=右。在(p-计数)处拆分
[左,类别(a),b]
其他的
[左,右]
结束
结束
结束

afaik这不是非常有效的实现,在字符串的一侧进行有偏差的拆分后,树将不会非常平衡。