I'd like to write a template binary search algorithm, which can search a template type element in a template type QList using an arbitary comparator, like this:
template<typename T,typename compare_less>
static int binary_search(QList<T>* list, T target) {
int low = 0;
int high = list->count()-1;
while (low <= high) {
int middle = low + (high - low)/2;
if (compare_less(*target, *list[middle]))
high = middle - 1;
else if (compare_less(*list[middle],*target))
low = middle + 1;
else
return middle;
}
return low;
}
Now how can I implement this correctly in order to make it work with QDateTime* template parameters? I'd like to call the function like this:
int index = binary_search<QDateTime*, ???>(dateTimeList,date);
Where dateTimeList is of type QList, date is of type QDateTime* and I really don't have any clue what to write in the place of question marks.
Can someone help me implement the algorithm correctly and show me how to call the algorithm with these arguments?
You shouldn't have to implement anything, if the Qt documentation is valid. Just use std::binary_search with your list's .begin() and .end(), and if you need to implement a comparator, do so and pass it to the STL algorithm.
Related
I'm trying to brush up on my C++ templating, and I'm running into the following issue. I'm implementing some basic in-place sorting methods, which I want to work for various types of data containers that can be indexed, and the elements of which can be compared. Specifically, the methods should work for both plain arrays, std::array, std::vector, etc. For some methods this is rather straightforward, like insertion sort:
template<typename T>
void insertion_sort(T& data)
{
if (std::size(data) < 2)
return;
for (int j = 1; j < std::size(data); ++j)
{
int i = j;
while (i > 0 && data[i - 1] > data[i])
{
swap_index(data, i, i - 1); // basic helper that swaps data at two indices
--i;
}
}
}
However, for some methods, I also need to know the actual type of the elements stored in data. An example is merge sort: I need to allocate some scratch space to be used for copying stuff around. I tried something like this:
template<typename T>
void mergesort(T& data)
{
typedef decltype(*std::begin(data)) inner_type;
inner_type* scratch = new inner_type[std::size(data)];
...
but that just gives me errors like "error C2528: 'scratch': pointer to reference is illegal".
In C# I'd just use something like where T : IComparable<T>, and have a parameter of type IList<T>, which seems to be closer to what I want to achieve: have a type T that is comparable, and as parameter some indexable collection of Ts. What is the "proper" way to achieve this with C++ templates? Or should I use something other than templates here? I want to sort the container in place, so I don't think I want to use something like iterators.
The clue is in the error message inner_type is a reference not the value type you are looking for. The following works for me (C++14 is required).
#include <type_traits>
typedef std::remove_reference_t<decltype(*std::begin(data))> inner_type;
I am attempting to compare algorithms. I am not familiar with the C++. I want to create a main where I will include the code below as a header. I don't completely understand what "template class Comparable" is though.
#include <vector>
using namespace std;
template <class Comparable>
void SelectionSort(vector<Comparable> & nums, int low, int high)
{
for (int i = low; i <= high-1; i++) {
int indexOfMin = i; // traverse the list to
for (int j = i+1; j <= high; j++) { // find the index of the
if (nums[j] < nums[indexOfMin]) { // next smallest item
indexOfMin = j;
}
}
Comparable temp = nums[i]; // swap the next smallest
nums[i] = nums[indexOfMin]; // item into its correct
nums[indexOfMin] = temp; // position
}
}
template <class Comparable> void SelectionSort(vector<Comparable> & nums)
{
SelectionSort(nums, 0, nums.size()-1);
}
Your main sort function there looks like this (snipping the "template" part for now):
void SelectionSort(vector<Comparable> & nums)
{
SelectionSort(nums, 0, nums.size()-1);
}
Looks like a normal sort function that acts on a vector of Comparables. But what is a Comparable? Well imagine if "Comparable" were nothing more than an alias for "int" (it's not, but imagine). Then you'd have this:
void SelectionSort(vector<int> & nums)
{
SelectionSort(nums, 0, nums.size()-1);
}
This is ordinary C++ code. It declares and defines a function that sorts a vector of ints. Pretty straightforward.
Comparable doesn't have a standard meaning like that. It is a term invented by the code in your question. It is declared by the text template <class Comparable>, approximately the way a variable is declared. It is something like a "type variable". An ordinary variable represents one of many values; a type variable represents one of many types.
template <class Comparable> void SelectionSort(vector<Comparable> & nums)
{
SelectionSort(nums, 0, nums.size()-1);
}
This code declares that Comparable is not automatically int, or float, or std::string, but rather may be any type at all. To use this function you must specify what type you want when you call the function. You can do it explicitly:
std::vector<int> someints;
SelectionSort<int>(someints);
(And this will make "Comparable" mean "int" after all, within that one call.)
Or you can leave out that extra specification and hope for the compiler to figure it out:
std::vector<int> someints;
SelectionSort(someints);
And you can use the same template for different types as much as you want; it is not "spent" in any sense by one use:
std::vector<int> someints, moreints;
std::vector<float> somefloats;
SelectionSort(someints);
SelectionSort(somefloats);
SelectionSort(moreints);
For a simple purpose like this, you can imagine that SelectionSort is a function that works on many types, not just one. But actually it is not a function. It is a whole family of potential functions, some of which may be instantiated by the compiler. The code just above calls SelectionSort three times, but with only two Comparable types, and so behind the scenes it creates two actual functions.
I've been talking about Comparable as a variable, but it can't vary WITHIN an instance of the template. You can't do Comparable=float within SelectionSort<int> or anything like that. It varies from one instance of the template to another, not within one instance. When the template is instantiated as a real function, Comparable is replaced by the type that was specified for it and then forgotten; that function doesn't "know" it is an instance of the template. It's just a function that happens to have angle brackets in its name. I think.
There are indeed some very powerful, complicated, mind-bending things that can be done with templates. But you probably don't need to know much about those for your purpose.
One more important basic point, though, is that there are also template classes. std::vector itself is one of them. They work in a roughly analogous way to template functions like SelectionSort: the header <vector> declares the vector template only once for all types, but then you can say std::vector<int> and then later std::vector<SomeClassIMade> and so on, and thereby automatically instantiate two (or more) actual classes. All these classes will work like a C++ vector is supposed to, but each one will only know how to handle its own specified element type, and will not understand any other.
I've been trying to write a templated function in C++ that can accept an array of any type and sort it. The sort used has to be a quick sort or a merge sort, but I'm having a lot of trouble with implementing either of these, as a quick sort header usually comes with a top and bottom parameter, and a merge sort comes with a first and last parameter. My function header looks like this: void mySort(T *array, int n)
So far I have this:
template <typename T>
void sort(T *a, int n)
{
int i = 0;
int j = n-1;
int tmp;
int pivot = a[(n-1)/2];
while (i <= j){
while (a[i] < pivot)
i++;
while (a[j] > pivot)
j--;
if (i<=j){
tmp = a[i];
a[i] = a[j];
a[j] = a[i];
i++;
j--;
}
}
if(0<j)
sort(a, j);
/*
if(i<right)
sort(
*/
}
I was trying to use a recursive call to sort, but I couldn't figure out how to call recursive for the right partition created, without a different parameter list.
Before answering the actual question: your code would benefit from factoring the partition code out of the body of the function! That way, you'd essentially call partition to determine the mid-point between the two arrays to be called, i.e., you'd have something like this:
template <typename T>
void sort(T* a, int n) {
T* mid = partition(a, n);
// ...
}
The idea is that [a, mid) contains all elements sorting smaller than the pivot and [mid, a + n) contains all elements sorting equal or greater than the pivot. All what remains is
Call sort() recursively with the two array, i.e.,
sort(a, mid - a);
sort(mid, (a + n) - mid);
Make sure sort() terminates if the array obtained is small than 2.
Of course, if you want your Quick Sort to be quick you'll also need to pull half a dozen or so tricks. Like:
Use Introsort to guarantee the complexity is O(n lg n) (e.g. together with Merge Sort).
Use Insertion Sort on small ranges.
Use an implementation of partition and insertion sort taking advantage of suitable sentinels.
Sort really sort ranges directly.
One of the things which oddly is rather futile to play with is choice of a pivot. As far as I can tell, using the middle element works as well as any more advanced technique (assuming optimizations like those mentioned above are implemented).
Separate the called function from the recursive function:
// recursive function
template <typename T>
void quicksort(T *a, int lo, int hi)
{
// ...
}
// called function
template <typename T>
void sort(T *a, int n)
{
if(n < 2)return;
quicksort(a, 0, n-1);
}
It seems that a priority queue is just a heap with normal queue operations like insert, delete, top, etc. Is this the correct way to interpret a priority queue? I know you can build priority queues in different ways but if I were to build a priority queue from a heap is it necessary to create a priority queue class and give instructions for building a heap and the queue operations or is it not really necessary to build the class?
What I mean is if I have a function to build a heap and functions to do operations like insert and delete, do I need to put all these functions in a class or can I just use the instructions by calling them in main.
I guess my question is whether having a collection of functions is equivalent to storing them in some class and using them through a class or just using the functions themselves.
What I have below is all the methods for a priority queue implementation. Is this sufficient to call it an implementation or do I need to put it in a designated priority queue class?
#ifndef MAX_PRIORITYQ_H
#define MAX_PRIORITYQ_H
#include <iostream>
#include <deque>
#include "print.h"
#include "random.h"
int parent(int i)
{
return (i - 1) / 2;
}
int left(int i)
{
if(i == 0)
return 1;
else
return 2*i;
}
int right(int i)
{
if(i == 0)
return 2;
else
return 2*i + 1;
}
void max_heapify(std::deque<int> &A, int i, int heapsize)
{
int largest;
int l = left(i);
int r = right(i);
if(l <= heapsize && A[l] > A[i])
largest = l;
else
largest = i;
if(r <= heapsize && A[r] > A[largest])
largest = r;
if(largest != i) {
exchange(A, i, largest);
max_heapify(A, largest, heapsize);
//int j = max_heapify(A, largest, heapsize);
//return j;
}
//return i;
}
void build_max_heap(std::deque<int> &A)
{
int heapsize = A.size() - 1;
for(int i = (A.size() - 1) / 2; i >= 0; i--)
max_heapify(A, i, heapsize);
}
int heap_maximum(std::deque<int> &A)
{
return A[0];
}
int heap_extract_max(std::deque<int> &A, int heapsize)
{
if(heapsize < 0)
throw std::out_of_range("heap underflow");
int max = A.front();
//std::cout << "heapsize : " << heapsize << std::endl;
A[0] = A[--heapsize];
A.pop_back();
max_heapify(A, 0, heapsize);
//int i = max_heapify(A, 0, heapsize);
//A.erase(A.begin() + i);
return max;
}
void heap_increase_key(std::deque<int> &A, int i, int key)
{
if(key < A[i])
std::cerr << "New key is smaller than current key" << std::endl;
else {
A[i] = key;
while(i > 1 && A[parent(i)] < A[i]) {
exchange(A, i, parent(i));
i = parent(i);
}
}
}
void max_heap_insert(std::deque<int> &A, int key)
{
int heapsize = A.size();
A[heapsize] = std::numeric_limits<int>::min();
heap_increase_key(A, heapsize, key);
}
A priority queue is an abstract datatype. It is a shorthand way of describing a particular interface and behavior, and says nothing about the underlying implementation.
A heap is a data structure. It is a name for a particular way of storing data that makes certain operations very efficient.
It just so happens that a heap is a very good data structure to implement a priority queue, because the operations which are made efficient by the heap data strucure are the operations that the priority queue interface needs.
Having a class with exactly the interface you need (just insert and pop-max?) has its advantages.
You can exchange the implementation (list instead of heap, for example) later.
Someone reading the code that uses the queue doesn't need to understand the more difficult interface of the heap data structure.
I guess my question is whether having a collection of functions is
equivalent to storing them in some class and using them through a
class or just using the functions themselves.
It's mostly equivalent if you just think in terms of "how does my program behave". But it's not equivalent in terms of "how easy is my program to understand by a human reader"
The term priority queue refers to the general data structure useful to order priorities of its element. There are multiple ways to achieve that, e.g., various ordered tree structures (e.g., a splay tree works reasonably well) as well as various heaps, e.g., d-heaps or Fibonacci heaps. Conceptually, a heap is a tree structure where the weight of every node is not less than the weight of any node in the subtree routed at that node.
The C++ Standard Template Library provides the make_heap, push_heap
and pop_heap algorithms for heaps (usually implemented as binary
heaps), which operate on arbitrary random access iterators. It treats
the iterators as a reference to an array, and uses the array-to-heap
conversion. It also provides the container adaptor priority_queue,
which wraps these facilities in a container-like class. However, there
is no standard support for the decrease/increase-key operation.
priority_queue referes to abstract data type defined entirely by the operations that may be performed on it. In C++ STL prioroty_queue is thus one of the sequence adapters - adaptors of basic containers (vector, list and deque are basic because they cannot be built from each other without loss of efficiency), defined in <queue> header (<bits/stl_queue.h> in my case actually). As can be seen from its definition, (as Bjarne Stroustrup says):
container adapter provides a restricted interface to a container. In
particular, adapters do not provide iterators; they are intended to be
used only through their specialized interfaces.
On my implementation prioroty_queue is described as
/**
* #brief A standard container automatically sorting its contents.
*
* #ingroup sequences
*
* This is not a true container, but an #e adaptor. It holds
* another container, and provides a wrapper interface to that
* container. The wrapper is what enforces priority-based sorting
* and %queue behavior. Very few of the standard container/sequence
* interface requirements are met (e.g., iterators).
*
* The second template parameter defines the type of the underlying
* sequence/container. It defaults to std::vector, but it can be
* any type that supports #c front(), #c push_back, #c pop_back,
* and random-access iterators, such as std::deque or an
* appropriate user-defined type.
*
* The third template parameter supplies the means of making
* priority comparisons. It defaults to #c less<value_type> but
* can be anything defining a strict weak ordering.
*
* Members not found in "normal" containers are #c container_type,
* which is a typedef for the second Sequence parameter, and #c
* push, #c pop, and #c top, which are standard %queue operations.
* #note No equality/comparison operators are provided for
* %priority_queue.
* #note Sorting of the elements takes place as they are added to,
* and removed from, the %priority_queue using the
* %priority_queue's member functions. If you access the elements
* by other means, and change their data such that the sorting
* order would be different, the %priority_queue will not re-sort
* the elements for you. (How could it know to do so?)
template:
template<typename _Tp, typename _Sequence = vector<_Tp>,
typename _Compare = less<typename _Sequence::value_type> >
class priority_queue
{
In opposite to this, heap describes how its elements are being fetched and stored in memory. It is a (tree based) data structure, others are i.e array, hash table, struct, union, set..., that in addition satisfies heap property: all nodes are either [greater than or equal to] or [less than or equal to] each of its children, according to a comparison predicate defined for the heap.
So in my heap header I find no heap container, but rather a set of algorithms
/**
* #defgroup heap_algorithms Heap Algorithms
* #ingroup sorting_algorithms
*/
like:
__is_heap_until
__is_heap
__push_heap
__adjust_heap
__pop_heap
make_heap
sort_heap
all of them (excluding __is_heap, commented as "This function is an extension, not part of the C++ standard") described as
* #ingroup heap_algorithms
*
* This operation... (what it does)
Not really. The "priority" in the name stems from a priority value for the entries in the queue, defining their ... of course: priority. There are many ways to implement such a PQ, however.
A priority queue is an abstract data structure that can be implemented in many ways-unsorted array,sorted array,heap-. It is like an interface, it gives you the signature of heap:
class PriorityQueue {
top() → element
peek() → element
insert(element, priority)
remove(element)
update(element, newPriority)
size() → int
}
A heap is a concrete implementation of the priority queue using an array (it can conceptually be represented as a particular kind of binary tree) to hold elements and specific algorithms to enforce invariants. Invariants are internal properties that always hold true throughout the life of the data structure.
here is the performance comparison of priority queue implementions:
I am writing a function to sort an array using heap sorting. So far I have:
template <typename Item, typename SizeType>
void heap_sort(Item data[], SizeType size) {
vector<int> v(data,data+size);
SizeType unsorted = size;
make_heap(v.begin(),v.end());
while(unsorted > 1) {
--unsorted;
swap(data[0], data[unsorted]);
reheapify_down(data,unsorted);
}
}
and:
template <typename Item, typename SizeType>
void reheapify_down(Item data[], SizeType size) {
SizeType current(0), big_child;
bool heap_ok = false;
while(!heap_ok && 2*current+1 < size) {
if(2*current+2 > size)
big_child = 2*current + 1;
else if(data[2*current+1] > data[2*current+2])
big_child = 2*current+1;
else
big_child = 2*current + 2;
if(data[current] < data[big_child]) {
swap(data[current],data[big_child]);
current = big_child;
}
else
heap_ok = true;
}
}
When I run the program, it outputs an incorrectly sorted array though. Is there something that I am just missing or some error that I overlooked?
Just a few suggestions.
First, I'd write a small proxy class that does nothing but let you use 1-based indexing on your collection. All the index math used in heaps assumes 1-based indexing, and it's a lot easier to compensate for 0-based indexing in one place than throughout all the code. As it stands right now, I have a hard enough time following the indexing to be sure your reheapify_down is correct. It's certainly the right general idea, but it would take a lot of work to be certain all the math is right.
Second, I'd write a check_heap (or whatever) that you can use to verify both your make_heap and your reheapify_down (as an aside, I'd decide on either "make_heap" or "heapify", so the names would be either "make_heap" and "remake_heap", or else "heapify" and "reheapify").
Beyond that, however, it's hard to be certain of the problem, especially since you haven't included the code for your make_heap in the question. If it isn't working correctly, the rest has no hope.