---100--- / \ 50 -300- / / \ 25 125 400 / \ \ 110 200 500 \ 250 / 225To delete 300 from this tree we must find the largest child in the left subtree, which is 250, and delete it. We will then replace 300 with 250. 250 has a single child, so to delete 250, we replace 250 with this child, producing the tree:
---100--- / \ 50 -300- / / \ 25 125 400 / \ \ 110 200 500 \ 225Then we replace 300 with 250, producing the final result:
---100--- / \ 50 -250- / / \ 25 125 400 / \ \ 110 200 500 \ 225Incorrect but worth 5 points: The course notes told you to delete the largest child from the left subtree and replace the deleted key with this child. If you instead deleted the smallest child from the right subtree, and replaced the deleted key with this child, you could get 5 points of partial credit. 400 is the smallest child in the right subtree, so you first delete 400. Since 400 has a single child, you replace 400 with 500, and you replace 300 with 400. The final tree in this case is:
---100--- / \ 50 -400- / / \ 25 125 500 / \ 110 200 \ 250 / 225
-300- / \ 175 400 \ 250 / \ 200 275The right rotation will cause 300 to become a right child of 175. In so doing the right subtree rooted at 250 will become an orphan because 300 is taking its place as 175's right child. Therefore 300 adopts 250 as its left child while keeping 400 as its right child. Note that it is ok for 300 to adopt 250 as its left subtree because all of the values in 250's tree are less than 300. The final tree becomes:
175 \ 300 / \ 250 400 / \ 200 275
20 / \ 10 40 / \ 8 16 / 12
In the tree below I have labeled each node with its height.
Since 20 is the first node whose subtree heights differ by
more than 1, it is the first node to violate the AVL condition.
20 h = 3
/ \
h = 2 10 40 h = 0
/ \
h = 0 8 16 h = 1
/
12 h = 0
We must use the zig-zag case. If we follow the route of the path followed for the insertion of 12, we see that we first went left, to 10, and then right, to 16. This left-right traversal is a zig-zag.
We need to use a left-right double rotation since we have a left-right zig-zag. We will first do a left rotation about the grandchild of 20, which is 16, and then do a right rotation about 16:
20 16 / \ / \ left rotation 16 40 right rotation 10 20 ------------> / --------------> / \ \ 10 8 12 40 / \ 8 12The left rotation makes 10 be a left child of 16. 12 becomes orphaned in this process and since 10's right child is now freed up, 10 adopts 12 as its right child. The right rotation makes 20 be a right child of 16. This rotation is fairly simple since no orphans are created in the process and therefore no adoptions are required.
a b c
b: as n gets large, the constant time program will be the fastest. Since we are unsure about the size of the input and are assuming the worst, we must assume that the input can be fairly large. Hence we prefer the constant time program, even though it has the largest constant.
|
|
|
|
For each fragment of code, please circle its Big-O running time:
Finally each iteration of the outermost loop has the increment and compare instructions, totaling 2 instructions, plus an execution of the middle loop, totaling 3n2 + 4n. The total instructions for each iterations is thus 3n2 + 4n + 2. There are n iterations of the outer loop, and hence the total number of instructions for the outer loop is n * (3n2 + 4n + 2), which is 3n3 + 4n2 + 2n. With Big-O notation we throw away all but the biggest term, which is 3n3, and then we throw away the leading constant, thus obtaining O(n3).
The second loop in the sequence prints the min array. Its loop body has a single cout statement, which we count as 1 instruction, and the loop increment and loop condition add 2 additional instructions. Hence the loop body requires 3 instructions. The loop executes a total of n times and hence takes 3n instructions.
When loops run sequentially as in this code fragment, we add the running times of the loops to get the running time of the code fragment. Adding the running times of the two loops gives us 4n2 + 6n instructions. Since Big O notation only cares about the biggest term and strips its leading constant, we end up with a Big O running time of O(n2).
Operation | Average Case | Worst Case |
---|---|---|
Inserting an element into a hash table that uses separate chaining |
O(1) | O(n) |
Finding an element in a binary search tree | O(log n) | O(n) |
Inserting an element into an AVL tree | O(log n) | O(log n) |
Adding an element to the front of a vector | O(n) | O(n) |
Removing an element from the top of a stack (i.e., pop) |
O(1) | O(1) |
A vector requires O(n) time to add an element to the front of the vector because all existing elements must be moved 1 element to the right.
a. array b. vector c. stack d. deque e. hash table f. list g. binary search tree h. AVL tree i. BTree
For each of the following questions choose the best answer from the above list. Assume that the size of an array is fixed once it is created, and that its size cannot be changed thereafter. Sometimes it may seem as though two or more choices would be equally good. In those cases think about the operations that the data structures support and choose the data structure whose operations are best suited for the problem. You may have to use the same answer for more than one question:
If you know the number of elements in advance, then you can pre-allocate an array and it will be more efficient then either a vector or a linked list.
If you don't know the number of elements in advance, then you have to use either a vector or a linked list. The vector is more efficient because you can store the elements contiguously in memory rather than linking them using pointers.
If elements are inserted in random order, then you can use a binary search tree since on average the tree will be balanced and the operations will require O(log n) time. Although binary search trees and AVL trees have the same Big O running time, binary search trees are faster when the trees are naturally balanced since they do not waste time on either checking for balance or rebalancing the tree.
If the keys are inserted in a nearly sorted order, then a binary search tree is likely to be lopsided and have its worst case running time of O(n) for insert, delete, and find. In this case we need to use an AVL tree to rebalance the tree after each insertion in order to guarantee an O(log n) running time.
When 375 gets inserted into the tree, it gets inserted into the leaf node containing the keys 325, 350, 400, and 425. Since the B-tree is of order 5, nodes can only hold 4 children. Hence we must split the leaf node and promote the middle element, which is 375, to the parent node. When we promote 375 to the parent node, it also overflows and must be split in two. We promote the middle element, which is 450, and it becomes the new root of the tree. The new tree is shown below:
// On the exam s was passed by-value but it should have been passed by
// reference since it is an object
bool palindrome(string &s, int begin, int end) {
// base case occurs when begin and end "cross over"
if (begin >= end)
return true;
// if the outer characters are equal, then check the rest of the string
else if (s[begin] == s[end])
return palindrome(s, begin+1, end-1);
else
return false;
}
int BSTree::recursive_size(BSTNode *n) { if (n == sentinel) return 0; else { int leftSize = recursive_size(n->left); int rightSize = recursive_size(n->right); return leftSize + rightSize + 1; } }
bool Queue::Delete(string key) { Qnode *currentNode, *prev; // As we move through the queue searching for the node to delete, prev // always points to the previous node that we visited. If we find a node // that contains the string, then we make this previous node point to // whatever the deleted node used to point to. prev = NULL; for (currentNode = first; currentNode != NULL; currentNode = currentNode->next) { // if we have found the node that contains the string, first check to // see if we are deleting the first node in the list. if (currentNode->s == key) { if (currentNode == first) { first = currentNode->next; // if first is now NULL, then the list had only one element and // therefore is now empty. Hence we must set last to be NULL as well if (first == NULL) last = NULL; } // else we are deleting a node from somewhere in the middle of the list else { prev->next = currentNode->next; // if we are deleting the last node in the list, then we need to // update the last pointer to point to prev if (last == currentNode) last = prev; } delete currentNode; return true; } else { prev = currentNode; } } // if we don't find the string in the loop, then it's not in the queue return false; }