如何拆分绳索树?

时间:2014-02-22 18:30:01

标签: string algorithm data-structures binary-tree

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

http://en.wikipedia.org/wiki/Rope_(data_structure)

连接很简单,但我仍然坚持拆分操作。维基百科文章指出:

  

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

找到角色并重新组合孤儿是没有问题的。但我不明白“向上移动树并移除任何正确的链接,从这些节点中减去K的重量”。示例在D处停止,但如果您逐字按照这些说明操作,则会继续执行B并删除D.此算法中的正确停止要求是什么?你如何避免只有一个(左或右)孩子的节点?

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

3 个答案:

答案 0 :(得分:1)

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

答案 1 :(得分:0)

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

首先确定向上旅行的起点。

A)如果您最终位于节点(节点A)的中间,请将字符串拆分为右侧字符索引并创建左右节点。这些新节点的父节点是节点A.左节点是您的起点。在旅行时,正确的节点将被添加到孤儿中。

B)如果你最终在一个节点的开头(字符方式)并且这个节点是一个正确的节点:拆分这个节点(=> orphan节点)并使用父节点作为起点。

C)如果你最终在一个节点的开头(字符方式)并且这个节点是一个左节点: 拆分此节点(=>孤立节点)并使用此节点作为起点。

D)如果你最终在一个节点的末尾(字符方式)并且这个节点是一个正确的节点: 使用父节点作为起点

D)如果你最终在一个节点的末尾(字符方式)并且这个节点是一个左节点: 使用此节点作为起点。

旅行期间 如果节点是左节点:向上移动并将其正确的兄弟节点添加到孤立列表中。

如果节点是右节点:向上移动(到父A),但对左兄弟节点不做任何操作。 (或者,因为到目前为止所有正确的节点都已被孤立,所以您可以将起点设置为父节点A的右侧节点。父节点A则是您的新起点。这可以避免一堆节点只有1个子节点。)

<强>后 Concat将所有累积的孤儿都变成了新的Node。这是新rootNode的正确部分。左侧部分是您旅行顺序的终点。

如果我错了,请纠正我。

答案 2 :(得分:0)

我在下面给出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