Week 3
Fill in big-O execution times Dynamic Array Bag Add: Contains: Remove: Linked list Bag Add: Contains: Remove: Ordered Array Bag Add: Contains: Remove:
Dynamic Array Bag: Add: Amortized O(1) Contains O(n) Remove O(n) Linked List Bag: Add: O(1) Contains: O(n) Remove: O(n) Ordered Array Bag Add: O(n) Contains: O(logn) Remove: O(n)
What will happen if an attempt is made to remove a value from an empty queue
It will raise an exception when an attempt is made on empty queue
Why do we need both front and back sentinels like this when implementing a deque on top of a linked list?
Remember, a deque must support four primary operations: adding to both the front and back and removing from both the front and back. Without using sentinels, each of these four operations would have to be implemented differently. For example, when adding to the front in a list without sentinels, we would have to explicitly update the list's head pointer upon each insertion, while on the other hand, adding to the back would require explicitly updating the list's tail pointer. However, when we use a list with front and back sentinels, we eliminate the need to implement different functionality in the deque's insertion operations and different functionality in its removal operations. In other words, both of the insertion operations (add to front and add to back) can use the exact same mechanics when we use sentinels, as can both of the removal operations.
Consider the following function - def unorderedSearch( self, value ): curNode = self.head while curNode is not None and curNode.data != value: if curNode.data == value : return True else :curNode = node.next return False Now suppose the linked list is sorted. What change will you make in the above function to take advantage of the sorted items? Your Answer:
The only change required is to change the second condition that terminates the loop early if we encounter a value larger than the target value.
We know that a circular buffer or circular queue is simply an array viewed as a circle instead of a line and we allow the values to wrap around back to the beginning of the array. When we resize our queue's underlying physical array, what strategy do we follow?
The value at logical index 0 in the old array gets copied to physical index 0 in the new array
Explain why the binary search algorithm speeds up the "contains" operation in a dynamic array, but does not speed up the "insert" and "removal" operations. Your Answer:
When using a dynamic array as the underlying container, if the elements are in sorted order we could use binary search to perform a very rapid sort test. Since the binary search operation is log n and all other operations would be O(n), that means the test is O(log n) overall. Inserting a new value into or removing a value from the ordered array won't be that straightforward.
Why is it difficult to implement a deque using the same dynamic array implementation
While a stack only has values added to and removed from one end of the array, a deque may have values added to or removed from the front or back. The problem is that removing or adding a value located at index 0 (unless it is the only value in the deque) is very difficult. For adding, you must shift all other values up one index in the array, and for removing, you must shift all other values back one index in the array. Otherwise, it would leave a "hole" in your array. Either operation would have O(n) algorithmic execution time, which is not ideal.
Consider the following implementation of the LinkedListIterator, which can be used to loop through a LinkedList. class SimpleLinkedListIterator: def __init__(self, head): self.head = head self.current = head def __iter__(self): return self # This is the meat of the iterator where we advance and stop when we hit a None node def __next__(self): # MISSING CODE
def __next__(self): if self.current is None:raise StopIteration cur = self.current self.current = self.current.next return cur
Define the two most common functions an iterator interface offers. Your Answer:
next()- moves iterator to next element has_next() - returns T/F if there's another element to iterate
Imagine you've been given the task of implementing a data structure to store an undo buffer for an application. This undo buffer should support a finite length 'undo' operation (e.g., you can only undo the last 20 steps) as well as support direct indexing to a particular step in your undo buffer (e.g., go back 5 steps). a) For the scenario described above, which ADT is appropriate? b) For the scenario described above, would you use the DynamicArray or LinkedList implementation of the ADT? Explain why. Your Answer:
a) One must use a deque, to allow removal of the 21st item from the back and the ability to add and remove of the top item. b) Dynamic array. The linked list structure does not allow direct indexing, so the entire list must be searched. A dynamic array allows for random memory access. Explanation: The ADT you should be looking for in a finite length undo buffer is a deque, because that allows you to add/remove the most recent actions from one end, and then remove the oldest actions from the other end once you hit the specified maximum limit of the deque. Using a deque with an underlying dynamic array data structure would be best implemented with a floating beginning index, which allows adding and removing to be executed in constant O(1) time. Also, undoing the most recent action step-by-step would be linear when using either the dynamic array or the linked list. But the question had a specific caveat of being able to directly index into a previous step. For example, if you were working with an image editor (like photoshop) that had a list of previous actions (your history). If you wanted to undo a series of actions, you just click on the last action/edit you want to step back into, which would undo everything after that action. This way you could directly index to a point in time of your recent edits and erase everything you did after that. This action could be several steps back in your history, so with one click, you can restore your project to a specific state without clicking "undo" multiple times.