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.hpp
namespace 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.hpp
Note: 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 -a
Will run the autograder and stop as soon as the first FAILURE is found
./autograder -t
Will 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 --failed
Will list all failed tests at the end of the autograder output
./autograder --passed
Will 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.
HeapUtils
PriorityQueue