PriorityQueue: a std::priority_queue class (but mostly just heap library functions)
Due: June 23, 2019 @ 11:59PM
Grab the handout tarfile from here: http://cs.millersville.edu/~wkillian/2019/summer/files/csci362/lab5b-priority-queue.tar
I've given you a mostly complete implementation of PriorityQueue. PriorityQueue is almost like std::priority_queue however, we do not handle certain forms of constructors or dealing with any underlying container. You are tasked with completing its implementation which relies heavily on HeapUtils.
The assignment is split into three files:
HeapUtils.hpp -- contains a bunch of heap utility functions you'll be implementingPriorityQueue.hpp -- contains the PriorityQueue classLike prior labs, it can be easy to get overwhelmed. But, if you work on one file at a time and complete the implementations of the functions in the specified order, you will make progress quickly!
Priority Queues give the user an option of specifying a custom comparator. This is similar to what we could do in Java with Comparator<E>
The C++ standard library provides a few useful comparator function objects (or functors).
namespace std
{
template <typename T = void>
struct less
{
bool
operator() (T const& a, T const& b) const noexcept
{
return a < b;
}
};
template <typename T = void>
struct greater
{
bool
operator() (T const& a, T const& b) const noexcept
{
return a > b;
}
};
} // end namespace std
Okay, this is weird/confusing. What's going on here? We are creating a struct that overloads the function call operator()
This means we can say something like this:
int a = /* initialized */;
int b = /* initialized */;
std::less<int> less_than;
// equivalent to saying: bool result = a < b;
bool result = less_than (a, b);
In fact, another cool way of explaining how things like this can work:
a < b /* is equivalent to saying */ operator<(a, b)
Throughout the HeapUtils file, there is reference to a Comparator called comp.
Anytime you would say a < b WHEN COMPARING VALUES IN THE HEAP just say comp(a, b)
| Operator | Comparator |
|---|---|
a < b |
comp (a, b) |
a > b |
comp (b, a) |
a <= b |
!comp (b, a) |
a >= b |
!comp (a, b) |
NOTE: solve in the listed order
HeapUtils.hppnamespace HeapUtils
{
namespace detail
{
/* [2] returns the parent's index
*
* p: the zero-based node index
*/
constexpr size_t
parent (size_t p) noexcept;
/* [2] returns the left child's index
*
* p: the zero-based node index
*/
constexpr size_t
left_child (size_t p) noexcept;
/* [2] returns the right child's index
*
* p: the zero-based node index
*/
constexpr size_t
right_child (size_t p) noexcept;
/* [2] returns the last interior node's index
*
* s: the size of the heap
*/
size_t
last_interior (size_t s) noexcept;
/* [4] returns the index of the maximum child of the node at index "pos"
*
* v: a vector representing a heap
* pos: the zero-based index of a node
* size: the logical size of the heap (may not be equal to v.size())
* comp: a comparator function object
*/
template<typename T, typename Comp>
constexpr inline size_t
max_child (std::vector<T> const& v, size_t const pos, size_t const size, Comp& comp) noexcept;
/* [10] down-heaps a node at index "curr" into place
*
* v: a vector representing a heap
* curr: the zero-based index of a node
* size: logical size of heap (may not be equal to v.size())
* comp: a comparator function object
*/
template<class T, class Comp>
void
sift_down (std::vector<T>& v, size_t curr, size_t size, Comp& comp) noexcept;
/* [10] up-heaps a node at index "curr" into place
*
* v: a vector representing a heap
* curr: the zero-based index of a node
* comp: a comparator function object
*/
template<class T, class Comp>
void
sift_up (std::vector<T>& v, size_t curr, Comp& comp) noexcept;
} // end namespace internal
/* [6] modifies v to become a heap. Starts at the last interior node and downHeaps
* each node until the root has been downHeaped
*
* v: a vector representing a heap
* comp: a comparator function object
*/
template<class T, class Comp>
void
make_heap (std::vector<T>& v, Comp& comp) noexcept;
/* [4] "inserts" a new element into the heap. The new element must reside
* at index v.size() - 1. Essentially calls upHeap(v.size() - 1)
*
* Preconditions:
* - v.size() > 0
* - v.back() was just added to the vector and may not be in place
*
* Postconditions:
* - v is a heap
*
* v: a vector representing a heap
* comp: a comparator function object
*/
template<class T, class Comp>
void
push_heap (std::vector<T>& v, Comp& comp) noexcept;
/* [4] "removes" the max element in the heap (and places it at index
* v.size() - 1). Essentially swaps front() with back() and then
* performs downHeap from index zero
*
* Preconditions:
* - v cannot be empty
*
* Postconditions:
* - v is a heap under the bounds [0, v.size() - 1)
*
* v: a vector representing a heap
* comp: a comparator function object
*/
template<class T, class Comp>
void
pop_heap (std::vector<T>& v, Comp& comp) noexcept;
/* [4] determines whether or not v is a heap (given the comparator)
* returns true IFF v is a valid heap
*
* v: a vector
* comp: a comparator object
*/
template<class T, class Comp>
bool
is_heap (std::vector<T> const& v, Comp& comp) noexcept;
} // end namespace HeapUtils
PriorityQueue.hppNote: this is not autograded
template<class T, class Comp = std::less<T>>
class PriorityQueue
{
public:
// [2] construct a priority queue from a comparator and underlying container
PriorityQueue (Comp const &comp, container_type const &c);
// [2] construct a priority queue from a comparator and underlying (r-value) container
explicit PriorityQueue (Comp const &comp, container_type &&c);
// [2] return True IFF the heap is empty
bool
empty () const;
// [2] return the size of the heap
size_type
size () const;
// [2] return the top of the heap
const_reference
top () const;
// [2] insert the new element "v" into the heap
void
push (value_type const &v);
// [2] insert the new element "v" into the heap
void
push (value_type &&v);
// [2] remove the top element from the heap
void
pop ();
};
Invoking make should create the autograder.
There can be a lot of output shown from the autograder. Some of it could be overwhelming at times. I recommend adding a couple of arguments to the autograder to help ease the pain.
./autograder -aWill run the autograder and stop as soon as the first FAILURE is found
./autograder -tWill list all of the tags for each of the tests. If you want to run a specific subset of tags you can do so by stating the following:
./autograder [access],[iterator]Will run all tests that have the access and iterator tags
./autograder --failedWill list all failed tests at the end of the autograder output
./autograder --passedWill list all passed tests at the end of the autograder output
Use good programming style:
Since this consists of multiple files, a tarball will be required for submission.
invoking make handin should create handin.tar
Please submit handin.tar to autolab
This lab is graded out of 75 points.
HeapUtilsPriorityQueue