Assorted ADTs
A list is an ordered collection, or sequence.
1 | interface List<E> { |
A set is a (usually unordered) collection of unique elements.
1 | interface Set<E> { |
A map is a collection of key-value mappings, like a dictionary in Python. Like a set, the keys in a map is unique.
1 | interface Map<K,V> { |
Stacks and queues are two similar types of linear collections with special behavior.A stack is a last-in, first-out ADT: elements are always added or removed from one end of the data structure. A queue is a first-in, first-out ADT. Both data types support three basic operations: push(e)
which adds an element, peek()
which returns the next element, and poll()
which returns and removes the next element.
Java defines an interface that combines both stacks and queues in the Deque. A deque (double ended queue, pronounced “deck”) is a linear collection that supports element insertion and removal at both ends.
1 | interface Deque<E> { |
Generally-speaking, a priority queue is like a regular queue except each element has a priority associated with it which determines in what order elements are removed from the queue. In Java, PriorityQueue
is a class, a heap data structure implementing the priority queue ADT. The priority is determined by either natural
order (E implements Comparable<E>
) or a supplied Comparator<E>
.
1 | class PriorityQueue<E> { |
Use them
a. Given an array of integers A and an integer k, return true if any two numbers in the array sum up to k, and return false otherwise. How would you do this? Give the main idea and what ADT you would use.
The fastest way to do this is with the help of a set (specifically, a HashSet, which has constant time add() and contains()). The key insight is that is that if a + b = x, then b = x - a. This means that we can look to see whether or not x - (current element) has been seen already. We can store every element that we look through in a set, and do a single pass through the array.
1 | public boolean twoSum(int[] A, int k) { |
b. Find the k most common words in a document. Assume that you can represent this as an array of Strings, where each word is an element in the array. You might find using multiple data structures useful.
Keep a count of all the words in the document using a HashMap<String, Integer>. After we go through all of the words, each word will be mapped to how many times it’s appeared. What we can do is to put all the words into a MaxPriorityQueue
1 | public static void topKPopularWords (String[] words, int k) { |
Mutant ADTs
a. Define a Queue class that implements the push and poll methods of a queue ADT using only a Stack class which implements the stack ADT.
Hint: Try using two stacks
1 | public class Queue<E> { |
b. Suppose we wanted a data structure SortedStack
that takes in integers, and maintains them in sorted order. SortedStack
supports two operations: push(int i)
and pop()
. Pushing puts an int onto our SortedStack
, and popping returns the next smallest item in the SortedStack
. For example, if we inserted 10, 4, 8, 2, 14, and 3 into a SortedStack
, and then popped everything off, we would get 2, 3, 4, 8, 10, 14.
The solution to this is very similar to that of the question above. Once again,we will have two stacks, A and B. A will hold all the items, and B will be our buffer again. This time, when we add something to the queue, we continue to pop items off from A and push it onto B until the next item that will be popped(we can access this via peek()) is greater than or equal to the item we’re adding to it. At that point, we can push our item onto A, and then pop everything from B and push them back into A. Thus, we maintain the sorted-ness of our SortedStack.
1 | public class SortedStack<Item implements Comparable<Item>> { |