delete()
can be implemented using rotations or value hoisting.
Which implementation is more efficient in terms of nodes touched? Justify your
answer.
Asymptotically, if delete()
searches for the value to be deleted, then it
doesn't make any difference if rotations or hoisting is used; the search takes
O(n) node touches. If delete()
is called with a pointer to the node to be
deleted, so no searching is required, it still doesn't make any difference if
rotations or hoisting are used because both dive down to a leaf, which requires
O(n) node touches.
Counting actual node touches, hoisting a value touches two nodes at each level (either once on the way down and once on the way up, or once when its value is hoisted to the parent and again when its child's value is hoisted into it). Rotating a node n touches four nodes: n and n's child, parent, and grandparent. Over the same path through a tree, hoisting performs half the node touches that rotating performs.
The answers, approximately verbatim, and in no particular order:
Value hoisting is more efficient than using rotations to implement a binary
tree delete()
because when we want to delete a root node it is easy1 to fill the root node by using a leaf
value and it is efficiently2 done
by using value hoisting as this implementation touches less number of
child-tree nodes.3
1: “Easy” means what?
2: “Efficiently” means what?
3: Hoisting touches fewer nodes than what? And how many nodes does it touch?
Value hoisting should be more efficient in terms of nodes touched.
Because binary-tree property4 is every children value should all smaller or all greater than their parents.5 That means if using rotations, implementation should go through each layer between the leaf node and the delete node. Value hoisting should just operate the delete node and the node6 should be hoisted.7
4: What's the binary-tree property?
5: Ah, you mean the binary-search-tree property, kind of. What does that have to do with this question?
6: Do you mean the node to be deleted? Or some other node?
7: Hoisted where?
Value hoisting is more efficient in terms of nodes touched. In a standard binary tree where you don't need to preserve any property. Value hoisting requires that you traverse down to a leaf node in order to find a value to hoist. This traversal at worst touches n nodes, where n is the number of nodes in the tree.
However, using rotation you need to touch multiple nodes for each rotation. First you need to touch the node which you are rotating and also one of its children to promote to its parent. You then also have to move any orphaned children to a new parent. If you don't rotate intelligently based on the balance of the children, then you will have at worst n rotations that each touch multiple nodes. This isn't asymptotically worse through.8
8: Why so?
9: Then how does hoisting work?
Using rotations, a worst-case delete from the root rotates the root to leaf. Every time rotate same same node was touched two times.10
Using hoisting, worst-case delete from root, first find one leaf. Nodes touched only are from root to leaf one time.11 Then switch value from leaf to root. Null out the leaf's parent. Left/right.
10: Do constants like 2 matter?
11: How many nodes is that?
Value hoisting would be more efficient. This is because the deletion of the unwanted data can be replaced by its child, being it has children.12 If not, it can be just deleted.
Rotations would require more steps.13
12: What happens to the child?
13: How many more steps?
delete()
function. In this case, less nodes are being touched,14 which helps
to keep the balance in the tree.15 It follows O(1) order.16 But in case of rotation more values might need to be
moved, which causes an unbalance in the tree.
14: Fewer nodes are being touched than what? What's the alternative?
15: Why can you assume balance for this question?
16: What is O(1)? It can't be deleting, can it?
Value hoisting of nodes in a tree is O(1) in number of operations.17
Rotating nodes is O(n) in number of operators. Due to the unbalanced structure of a binary tree you could touch all nodes.
Value hoisting being O(1) is more efficient.
17: What are operations? Is that what you're supposed to be counting?
Binary-tree delete()
Using rotations
18: Which subtree?
19: Ok, but how many nodes does that touch?
20: By what accounting?
delete()
on a binary tree. Using rotations would give O(log n) behavior
because you are only touching half the nodes in a particular subtree.21 However, value hoisting requires you to dive
down and find a leaf node,22 which results in O(n) or linear behavior.
21: Why half the nodes?
22: Don't rotations have the same requirement?
add()
, remove()
, and find()
on the AVL-- tree in terms of
node touches.
Because an AVL-- tree is balanced, add()
is still O(log n), either because
new values are still added at the leaves, or because new values are added at
the root and may require a full trip down the tree to restore
balance. .footnote[ We didn't cover this detail in class, and you weren't
expected to know it. ] An AVL-- tree isn't a binary search tree, which means
find()
has O(n) worst-case performance because it may have to search the
entire tree. delete()
also has O(n) worst-case performance, unless the
delete()
argument points directly to the value in the tree, in which case it
has O(log n) worst-case performance.
The answers, approximately verbatim, and in no particular order:
add()
is constant, O(1), since you can add nodes to the root of the
tree.1 You would just touch the existing root to make it a child of
the new root.
remove()
is linear, O(n), since you have to first find the node that
contains the value to remove (assuming it exists), then move it down to a leaf
node to remove it.
find()
is linear, O(n), since in the worst case the value doesn't exist,
you touch every node trying to find it before you can conclude that it doesn't
exist.
1: An AVL-- tree is still a height-balanced tree, and that has to be maintained.
The asymptotic performance of the operations add()
, remove()
, and
find()
on the AVL tree2 is O(log n) or
logarithmic behavior. For the operation add()
, you can add the value as a
leaf, and use node rotations to make the tree well balanced. For the operation
remove()
, you also have to use node rotations to make the value you want to
remove a leaf, while keeping the tree balanced. In the worst case, these
operations will touch half of the nodes in the tree.34
2: An AVL-- tree.
3: How could they touch half the nodes in a tree?
4: And what about find()
?
In an AVL tree5 with n nodes:
For find()
: it has linear worst case performance O(n) as it has to
search all the nodes in the tree to find the value.
For remove()
: It has a linear worst-case performance O(n) as it has to
find the value and then delete the value from that particular node.
For add()
: It has a linear-average case6
performance log(n) as it has to find the root node and add the new value
to it.
5: An AVL-- tree.
6: Worst-case performance.
add()
,remove()
,find()
,7: Where does a heap get involved? Are you thinking of a treap?
To add values into an AVL-- tree, we just add a new value to a leaf node and make sure any two sibling subtrees differ in height by at most 1.8
To find a node, we need to go through each node on the tree. It costs O(n) in worst case.
To remove a node, we meed to find a leaf and copy the value of the leaf node to become a node we want to rename.9
8: And how much does that cost?
9: Cost?
add() | = | O(log n) | 10
remove() | = | O(log n) |
find() | = | O(log n) |
10: Why is this so?
To add()
a value depending on the structure of the AVL-- tree we would start
at end of the tree (leaf nodes) and if it's a heap max11 we would implement a method of rotations, where the
value is added.
To remove()
a node the operation would be quite different.12 The node that is to be removed would be replaced by the
existing node that came after it.13
The find()
node would be traversing starting from
11: What do heaps have to do with anything?
12: Different from what?
13: And what happens to the children of the node being used as a replacement? And what's the performance?
14: An AVL-- tree.
15: You mean the difference in children heights is at most one, right?
16: The structure of what? The AVL-- tree?
17: How does that result in O(1) performance?
18: Why is that?
expected | add() take O(1) operation. |
remove() take O(n) operation. | |
find() take O(n) operation. |