Description
In this assignment we will be implementing a partial and modified version of a Rope. Due to our modifications, other sources may not use the same terminology or may describe implementation details that are not
relevant to our HW8 implementation – in fact even Wikipedia will differ on some specifics.
For this reason
you should avoid looking at code or online resources about ropes, and cannot use someone else’s code in this
assignment.
Lecture/lab code is okay to use though. You should read the entire assignment before beginning
your work.
You should also make sure you understand the basic concepts discussed at the end of Lecture 18.
The idea behind a rope is that normally, inserting into and erasing from the middle of a string (character
array) is very inefficient, for the same reasons that these operations are slow on an STL vector.
Instead, we
can represent a string inside a binary tree where each node has a weight (a non-negative integer) and a value
(zero or more characters). Any node that is not a leaf will have an empty string as its value – only the leaves
hold pieces of the string. The weight of a leaf is the length of its value. The weight of any non-leaf is the
number of characters in the leaves of its left subtree.
Our assignment notably does not implement operator– for rope iterators, nor does it implement erase() or
insert(). Much like an STL map or set, ropes are best implemented with a balanced tree. However, since
we have not discussed balanced trees and this assignment will be large enough without such details, we will
ignore the balanced tree requirement. The README has a section where you will discuss performance and
the impact of not having a balanced tree on performance.
Provided Files
We have provided several files which contain further instructions. main.cpp contains some simple tests in
BasicTests() for various functions you will need to write, and the expected output is shown in output basic.txt.
Your own additional testing should be added to StudentTests(). We have also given you Rope.h which you
can modify as you see fit, Rope student.cpp which has the functions you need to write, and Rope provided.cpp
which you should not change at all. We will use our own copy of Rope provided.cpp when testing on Submitty.
You can add additional functions to the .h file and Rope student.cpp – we will always use your version of
these two files. Pay particular attention to the comments in Rope student.cpp!
Hints
You may find it beneficial to borrow code from our partial implementation of the ds set class and associated
helper classes. Focus on writing the destructor first and then you can start working through BasicTests()
step by step.
Carefully consider whether it makes more sense to do a function iteratively or recursively.
Keep in mind for index() you must write an iterative version. You may want to add some private helper
functions to the Rope class. The most challenging function will be split(), and breaking the work up can
make debugging easier. You might also want to call the provided print functions a lot when debugging to
quickly visualize your rope.
Submission
Submit your main.cpp, Rope.h, Rope student.cpp, and README.txt. Dr. Memory will be used on this
assignment and you will be penalized for leaks and memory errors in your solution. If you get at least 3
points on test case 13 by the end of Wednesday, you can submit on Friday without being charged a late day.
Please remember that all submissions are still due by the end of Saturday.
concat()
concat() adds the right-hand (rhs) rope to the left-hand (lhs) rope by creating a new root. In our case, this
is written as lhs.concat(rhs).
2 4
na me_i
2
6
6
3
Hello_ my_
9
left root right root
new root
Starting with two Ropes, left and right we do the
following: (Keep in mind we may need to copy the
right subtree, look in the provided code for clues to
the correct behavior for our case!)
1. Create a new node which will be the new root
2. Connect it to the root of the left rope and update
weight
3. Connect it to the root of the right rope
4. Set the new node to be the root
index()
index(i) for Ropes works like operator[i] would for a string or vector, it returns the i
th character, with
the first character being at i = 0.
The blue arrows/text show the search and how i
changes for i = 9.
22
9
6
6
3
2 6 4 1
2 1
6
my_
na me_i s
Hello_
9
0
0
0
[0]
_Simon
To find the character at index i = 9 (assuming we start
at index 0):
1. Start at the root and decide to go left since the
root’s weight is 22 meaning the left subtree has
indices 0-21
2. Next, decide to go right since the current node’s
weight is 9, meaning the left subtree has indices
0-8. Subtract the root’s weight (9) from i, so now
i = 0. (Anytime we go right we must subtract
the weight of the current node. Think about
why.)
3. Since i = 0 we will always go left. A search will
continue making choices to go left or right until it
hits a leaf. (You can consider why this is always
correct.)
4. At the leaf, return the character at index i, in
our case that’s “na”[0] which is ‘n’.
See next page for split() details
We have adopted a couple examples from the Wikipedia article on ropes. We provide these links only to comply with the
Creative Commons Attribution-Share Alike 3.0 Unported license. Remember that the Wikipedia article describes some functions
differently and you should not view it for homework assistance: [1] [2] [3]
2
split()
4
X
X
1
2
9
6
6
3
2
2
my_
na
Hello_
2
22
9
6
6
3
2 6 1
2 1 6
my_
na
s
Hello_
1
m e_i 3 1
11
9
6
6
3
2 6 4 1
2 1
my_
na me_i
s
Hello_
11
2
_Simon
22
9
6
6
3
2 6 4 1
2 1 6
my_
na me_i
s
Hello_
_Simon
_Simon
me_i 4
1
6
1
s _Simon
To split, we first find the index we want to split
at. Remember that for a split at index
i, indices 0
through (i-1) stay in the left rope, and everything
else goes to the right.
If
i is in the middle of a node’s value, then we have
to cut the node into two pieces. For example, if
we chose
i = 12, then we would want the “e” in
“me i” to be the first part of the new righthand
rope.
But this is in the middle of a leaf. To fix
this, give the leaf two new children, a left child
with “m”, and a right child with “e i”, and then
update weights accordingly. We can then use the
new “e i” leaf to start our actual split.
In the lower example, we consider i = 11 so that we do not have to deal with this.
1. First we find
i = 11 which is the first letter
in “me i”. Following the rules of index()
,
i
will have been adjusted to be 0. So the leaf
will not need to be split.
2. Next, we disconnect the leaf from the
original (lefthand) rope.
3. Going up the rope, for any node where the
right subtree will completely be in the new
(righthand) rope, we also disconnect the
right child from the original rope.
4. Going from left to right we concatenate the
root of each subtree that we disconnected,
since they will all be small ropes.
In this
example we split the
X
1 and then
X
2
, so
we just concat no copy(X
1
,
X
2
). (This is
different from the concat() described earlier
in that we never want to copy the righthand Rope, we always want to just adjust
pointers when using it to help with split()
!
5. Finally, update the weights of all nodes
(unless you did this when you were disconnecting/concatenating).
3