问题
I came across a rope tree as an alternative data structure for a string.
http://en.wikipedia.org/wiki/Rope_(data_structure)
To concat is easy, but I am stuck on the split operation. The wikipedia article states:
For example, to split the 22-character rope pictured in Figure 2.3 into two equal component ropes of length 11, query the 12th character to locate the node K at the bottom level. Remove the link between K and the right child of G. Go to the parent G and subtract the weight of K from the weight of G. Travel up the tree and remove any right links, subtracting the weight of K from these nodes (only node D, in this case). Finally, build up the newly-orphaned nodes K and H by concatenating them together and creating a new parent P with weight equal to the length of the left node K.
Locating the character and recombining the orphans is no problem. But I don't understand the "Travel up the tree and remove any right links, subtracting the weight of K from these nodes". The example stops at D, but if you follow these instructions verbatim you would continue onto B and remove D as well. What is the correct stopping requirement in this algorithm? And how do you avoid nodes with only one (left or right) child?
A pseudo-code algorithm explaining this part would help tremendously.
回答1:
The wikipedia article is not very explicit. If the current node is X and its parent is Y you would only travel up if X is the left child of Y. Visually you're going up and to the right as far as you can.
回答2:
After some tinkering and consideration, I think the rules should be this:
First determine the starting point for traveling upwards.
A) If you end up in the middle of a node (node A), split the string at the right character index and create a left and right node. The parents of these new nodes is node A. The left node is your starting point. The right node will be added to the orphans while traveling up.
B) if you end up at the beginning of a node (character wise) and this node is a right node: split of this node (=> orphan node) and use the parent node as your starting point.
C) if you end up at the beginning of a node (character wise) and this node is a left node: split of this node (=> orphan node) and use this node as your starting point.
D) if you end up at the end of a node (character wise) and this node is a right node: use the parent node as your starting point
D) if you end up at the end of a node (character wise) and this node is a left node: use this node as your starting point.
During traveling: If the node is a left node: move upwards and add its right sibling node to the list of orphans.
If the node is a right node: move upwards (to parent A), but do nothing with the left sibling node. (Or, because all the right nodes up to this point have been orphaned, you could set the starting point as the right node of the parent node A. The parent node A is then your new starting point. This avoids a bunch of nodes with only 1 child node.)
After Concat all the accumulated orphans into a new Node. This is the right part of your new rootNode. The left part is the endpoint of your travel sequence.
Please correct me if I am wrong.
回答3:
I give Ruby code below. It's as close to executable pseudocode as you'll get. If Ruby isn't right for your implementation, you can always use it as a prototype. If you don't like the recursion, it's easy enough to use standard transformations to make it iterative with an explicit stack.
The natural implementation of split is recursive. The interesting case is near the bottom of the code.
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
来源:https://stackoverflow.com/questions/21958560/how-to-split-a-rope-tree