Heaps and PQs(The Priority Queue)
The Priority Queue Interface
If we want to cared more about quickly finding the smallest or largest element instead of quickly searching
Now we come to the Abstract Data Type of a Priority Queue. To understand this ADT, consider a bag of items. You can add items to this bag, you can remove items from this bag, etc. The one caveat is that you can only interact with the smallest items of this bag.
/** (Min) Priority Queue: Allowing tracking and removal of the
* smallest item in a priority queue. */
public interface MinPQ<Item> {
/** Adds the item to the priority queue. */
public void add(Item x);
/** Returns the smallest item in the priority queue. */
public Item getSmallest();
/** Removes the smallest item from the priority queue. */
public Item removeSmallest();
/** Returns the size of the priority queue. */
public int size();
}
Summary
- Priority Queue is an Abstract Data Type that optimizes for handling minimum or maximum elements.
- There can be space/memory benefits to using this specialized data structure.
- Implementations for ADTs that we currently know don’t give us efficient runtimes for PQ operations(优先队列).
举例子,对于查找队列中最小的值。最大的值等等:
Ordered Array:
1. add : Θ(N)
2. getSmallest : Θ(1)
3. removeSmallest : Θ(N)
Bushy BST :
1. add : Θ(logN)
2. getSmallest : Θ(logN)
3. removeSmallest : Θ(logN)
HashTable
1. add : Θ(1)
2. getSmallest : Θ(N)
3. removeSmallest : Θ(N)
- A binary search tree among the other structures is the most efficient
Heap Structure
We will define our binary min-heap as being complete and obeying min-heap property:
- Min-heap: Every node is less than or equal to both of its children
- Complete: Missing items only at the bottom level (if any), all nodes are as far left as possible.
Heap Operations
The three methods we care about for the PriorityQueue ADT are add , getSmallest , and removeSmallest . We will start by conceptually describing how these methods can be implemented given our given schema of a heap.
-
add : Add to the end of heap temporarily. Swim up the hierarchy to the proper place. Swimming involves swapping nodes if child < parent
-
getSmallest : Return the root of the heap (This is guaranteed to be the minimum by our min-heap property)
-
removeSmallest : Swap the last item in the heap into the root. Sink down the hierarchy to the proper place. Sinking involves swapping nodes if parent > child. Swap with the smallest child to preserve min-heap property.
Tree Representation
Approach 1a, 1b, and 1c
We will create mappings between nodes and their children
- In approach Tree1A, we consider creating pointers to our children and storing the value inside of the node object. These are hardwired links that give us fixed-width nodes. We can observe the code:
public class Tree1A<Key> {
Key k; // e.g. 0
Tree1A left;
Tree1A middle;
Tree1A right;
...
- Alternatively, in Tree1B, we explore the use of arrays as representing the mapping between children and nodes. This would give us variable-width nodes, but also awkward traversals and performance will be worse.
public class Tree1B<Key> {
Key k; // e.g. 0
Tree1B[] children;
...
- Lastly, we can use the approach for Tree1C. This will be slightly different from the usual approaches that we’ve seen. Instead of only representing a node’s children, we say that nodes can also maintain a reference to their siblings
public class Tree1C<Key> {
Key k; // e.g. 0
Tree1C favoredChild;
Tree1C sibling;
...
Approach 2
Recall the Disjoint Sets ADT. The way that we represented this Weighted Quick Union structure was through arrays. For representing a tree, we can store the keys array as well as a parents array. The keys array represent which index maps to which key, and the parents array represents which key is a child of another key.
public class Tree2<Key> {
Key[] keys;
int[] parents;
...
It’s time to make a very important observation! Based on the structure of the tree and the relationship between the array representations and the diagram of the tree, we can see:
- The tree is complete. This is a property we have defined earlier.
- The parents array has a sort of redundant pattern where elements are just doubled.
- Reading the level-order of the tree, we see that it matches exactly the order of the keys in the keys array.
: Store keys in an array. Don’t store structure anywhere.
- To interpret array: Simply assume tree is complete.
- Obviously only works for “complete” trees.
Challenge: Write the parent(k) method for approach 3.
public int parent(int k) {
return (k - 1) / 2;
}
Approach 3b![Approach 3](https://i-blog.csdnimg.cn/blog_migrate/7d1cd09b5cf47f1f04c0a9c93145fc9d.png)
Comparing to alternative implementations for The Priority Queue
- Heap operations are amortized analysis, since the array will have to resize (not a bigdeal)
- BST’s can have constant time getSmallest if pointer is stored to smallest element
- Array-based heaps take around 1/3rd the memory it takes to represent a heap using approach 1A (direct pointers to children)
Code for MinPQ interface
/** (Min) Priority Queue: Allowing tracking and removal of the
* smallest item in a priority queue. */
public interface MinPQ<Item> {
/** Adds the item to the priority queue. */
public void add(Item x);
/** Returns the smallest item in the priority queue. */
public Item getSmallest();
/** Removes the smallest item from the priority queue. */
public Item removeSmallest();
/** Returns the size of the priority queue. */
public int size();
}