/*** Question 1 Selection Sort: easy to code, so good if you have to write the code yourself. O(n) swaps, so good if items being sorted are big (e.g., a struct with lots of data) Insertion Sort: good on nearly sorted data, it's O(n) on a sorted vector. Stable also. Bubble Sort: Good if someone gives you $1,000,000 to use it, otherwise not worth much ----- Recurrence is T(n) = 2T(n/2) + O(n) solution is O(n log n) for linked lists the recurrence stays the same since finding middle is O(n) and merging is O(n), so there's still O(n) work. Thus the complexity is the same. Question 2 Part A -- Initially add any value >= 20 Part B -- * 7 11 8 15 12 10 23 18 17 20 Part C -- * 8 12 10 15 20 17 23 18 Part D -- Insert is O(1) average case, O(log n) worst case. The first loop calls insert n times, where n is the number of elements in the vector. The complexity of the first loop is (worst case) log 1 + log 2 + ... + log n = log(n!) === n log n The second loop is the same thing, deletemin is O(log n) where n is # items in the heap In the average case the first loop is O(n) [n x O(1)] and the second loop is still O(n log n) so the overall complexity is still O(n log n) Part E -- The complexity doesn't change if a balanced tree is used since insert and deletemin are both O(log n) [n # items in tree] Question 3 ---- The tree is * / \ * e / \ * a / \ * t / \ * r / \ s o Part B -- Using an int won't work because then the max root-leaf path in a huffman tree has length 32 since there are 32 bits in an int. As the tree above shows, it's possible to have "bad" trees. Using a string there is no limit since a string can have an arbitrary number of 0's and 1's Part C -- In an unsorted vector insert is O(1) and deletemin is O(n) whereas these are both O(log n) in a heap [worst-case]. However, in huff n is roughly 256, this isn't very big and the difference won't be so noticeable. Of greater importance, most of the time in huff is spent reading the file twice, making the tree accounts for a negligible part of the overall running time. Part D -- Buffering of output, extra bits can be written, unhuff uses the PSEUDO_EOF character to know when to stop without processing the extra bits Question 4 -- Part A -- Sort using merge sort, e.g., so two sorts is O(n log n) Comparing is then O(n), so total time is O(n) + O(n log n) == O(n log n) Part B -- Insert into a bst is O(log n) [n # items in tree], so to insert n items is O(n log n) (see reasoning above in 2D). Then every item in one tree/map is compared to count/item in the other map this is n * log n since lookup in the map (map.get) is O(log n) The total time is n log n, twice to make maps and n log n, twice to check maps against each other O(n log n) Part C --- Map operations are O(1), done n times, so make maps: O(n), twice check maps: O(n), twice total is O(n) Problem 5 --- numNodes is O(n) since recurrence is T(n) = 2T(n/2) + O(1) findk has recurrence T(n) = T(n/2) + O(n) since only ONE recursive call is made, the solution is O(n) Part B -- Copying into a vector is O(n) using in-order traversal. If findk is called only once, probably not worth it because of extra storage and copy time. But if findk will be called multiple times it will be worth it if storage isn't too much of an issue. Problem 6 -- X | | X | O | X | O | X | O | ---+---+--- ---+---+--- ---+---+--- ---+---+--- | | | | X | | X | 0 | ---+---+--- ---+---+--- ---+---+--- ---+---+--- | | | | | | | | | | | | X | | X | O | ---+---+--- ---+---+--- ---+---+--- ---+---+--- X | | X | O | X | O | X | 0 | ---+---+--- ---+---+--- ---+---+--- ---+---+--- | | | | | | | | This shows two ways to get to the same board, rather than recurse for the board twice, its score is stored and retrieved the second time. Since hashing is O(1) to do a lookup, the savings is very pronounced. **/