Implementing a sort class using templates - c++

I posted last night about an array class, and now I'm having trouble with a sorting class. About half of the functions are defined, either by the professor or by the algorithms he gave us, but a lot of the definitions are confusing me. I'm not sure what makes them any different from the ones above it.
#ifndef SORTS_H
#define SORTS_H
#include <iostream>
#include <string>
#include "Array.h"
using namespace std;
template <class T>
void insertionSort(Array<T> &a);
template <class T>
void selectionSort(Array<T> &a);
template <class T>
void selectionSort(T a[], int n);
template <class T>
void mergesort(T *input, int size);
template <class T>
void mergesort(T *input, int left, int right, T *scratch);
template <class T>
T less(T x, T y);
template <class T>
void mergesort(Array<T> &input, int left, int right, Array<T>&scratch);
template <class T>
void mergesort(Array<T> & input);
Array<int> & random(int n);
template <class T>
void selectionSort(T a[], int n) {
int i, j, tmp;
int min_idx = 0;
for (size_t i = 0; i < n-1; i++) {
min_idx = i;
for (size_t j = i+1; j < n; j++) {
if (a[j] < a[min_idx]) {
min_idx = j;
}
tmp = a[i];
a[i] = a[min_idx];
a[min_idx] = tmp;
}
}
}
template <class T>
void selectionSort(Array<T> &a) {
int tmp;
int min_idx = 0;
for (int i = 0; i < a.getSize() - 1; i++) {
min_idx = i;
for (int j = i + 1; j < a.getSize(); j++) {
if (a[j] < a[min_idx]) {
min_idx = j;
}
tmp = a[i];
a[i] = a[min_idx];
a[min_idx] = tmp;
}
}
}
template <class T>
void insertionSort(Array<T> &a) {
// put your code here
}
template <class T>
bool sorted(Array<T> a) {
for (int i = 1; i < a.getSize(); i++)
if (a[i - 1] > a[i]) return false;
return true;
}
Array<int> & random(int n) {
Array<int> *tmp = new Array<int>(n);
for (int i = 0; i < n; i++)
(*tmp)[i] = rand() % 1000;
return *tmp;
}
template <class T>
T less(T x, T y) {
if (x < y) {
return x;
}
else {
return y;
}
}
template <class T>
void mergesort(T *input, int left, int right, T *scratch) {
if (right == left + 1) {
return;
}
else {
int i = 0;
int length = right - left;
int midpoint_distance = length / 2;
int l = left, r = left + midpoint_distance;
mergesort(input, left, left + midpoint_distance, scratch);
mergesort(input, left + midpoint_distance, right, scratch);
/* merge the arrays together using scratch for temporary storage */
for (i = 0; i < length; i++) {
/* Check to see if any elements remain in the left array; if so,
* we check if there are any elements left in the right array; if
* so, we compare them. Otherwise, we know that the merge must
* use take the element from the left array */
if (l < left + midpoint_distance &&
(r == right || min(input[l], input[r]) == input[l])) {
scratch[i] = input[l];
l++;
}
else {
scratch[i] = input[r];
r++;
}
}
/* Copy the sorted subarray back to the input */
for (i = left; i < right; i++) {
input[i] = scratch[i - left];
}
}
}
template <class T>
void mergesort(T *input, int size) {
int *scratch = new int[size];
mergesort(input, 0, size, scratch);
delete [] scratch;
}
template <class T>
void mergesort(Array<T> &input, int left, int right, Array<T>&scratch) {
if (right == left + 1) {
return;
}
else {
int i = 0;
int length = right - left;
int midpoint_distance = length / 2;
int l = left, r = left + midpoint_distance;
mergesort(input, left, left + midpoint_distance, scratch);
mergesort(input, left + midpoint_distance, right, scratch);
/* merge the arrays together using scratch for temporary storage */
for (i = 0; i < length; i++) {
/* Check to see if any elements remain in the left array; if so,
* we check if there are any elements left in the right array; if
* so, we compare them. Otherwise, we know that the merge must
* use take the element from the left array */
if (l < left + midpoint_distance &&
(r == right || min(input[l], input[r]) == input[l])) {
scratch[i] = input[l];
l++;
}
else {
scratch[i] = input[r];
r++;
}
}
/* Copy the sorted subarray back to the input */
for (i = left; i < right; i++) {
input[i] = scratch[i - left];
}
}
}
template <class T>
void mergesort(Array<T> &input) {
// put your code here
}
#endif
I also noticed that there is a void insertionSort(Array<T> &a); function, but the algorithm I was given is:
void insertionSort(int a[], int n){
int tmp;
int i, j;
for (i = 1; i < n; i++) {
tmp = a[i];
for (j = i - 1; j >= 0; j--)
if (a[j] > tmp) a[j + 1] = a[j];
else break;
a[j + 1] = tmp;
}
}
Am I supposed to implement this the same way, just replacing int a[] with, say... &arr? I'm guessing since this includes array.h and the array class has T * arr;, I should be pointing to the address of that array? Would this also work for each definition that has the address operator in its parameter?

The difference is one, as you say, takes a typical int array a[], but how would you know the size? So this version requires the user to send it to the function with n as the number of elements. In your Array class you provide a size so there's no need for it. In general you're providing overloads for multiple situations.
I'm not sure what you mean by replacing int a[] w/ &arr, the signature is there, use what was given to you unless you're supposed to change it.
If you go back to your question about the Array class you can see an answer which uses the reference just as you would normally, i.e,
template <class T>
Array<T>::Array(const Array &other) : size(other.size), arr(new T[size])
{ // ^^^^^^
for (int i = 0; i < size; ++i)
arr[i] = other.arr[i];
} // ^^^^^
now apply it to this situation.
Also,
template <class T>
void selectionSort(Array<T> &a) {
// I'm not sure I understand what this is supposed to do that's different from the above selectionSort.
// I know that & is the reference operator, so should I just return whatever &a is?
}
You won't be returning anything here considering void as the return and the use of the reference. You pass by reference as opposed to by value so that what you do in the function is persistent. You could choose to pass back the sorted array and not use a reference but I'm fairly certain it'd be slower overall considering the assignment and copy. That's why the example from your other question is using const Array &other. It prevents the entire array, which may be large, from being copied and sent to the function as well as being changed.

Related

Heapsort with a selectable amount of children

Im trying to write some code that could sort an array using heapsort. The heap have two children right now but i want the user to be able to choose the amount of children in the heap(d in the function heapsort).
Question: How do i make the function heapsort able to recieve a number (d) from the user and sort the array with heapsort with that amount of children?
template <typename T>
void heapify(T arr[], int n, int i) {
int biggest = i;
int leftChild = 2 * i + 1;
int rightChild = 2 * i + 2;
if (leftChild < n && arr[leftChild] > arr[biggest])
{
biggest = leftChild;
}
if (rightChild < n && arr[rightChild] > arr[biggest]) {
biggest = rightChild;
}
if (biggest != i) {
swap(arr[i], arr[biggest]);
heapify(arr, n, biggest);
}
}
template <typename T>
void heapsort(T arr[], int n, int d)
{
for (int i = n / 2 - 1; i >= 0; i--) {
heapify(arr, n, i);
}
for (int i = n - 1; i > 0; i--) {
swap(arr[0], arr[i]);
heapify(arr, i, 0);
}
}
Just because you made it so easy:
template <typename T>
void heapify(T arr[], int n, int d, int i) {
int biggest = i;
int childrenStart = d * i + 1;
int childrenEnd = childrenStart + d;
if (childrenEnd > n) {
childrenEnd = n;
}
for (int child = childrenStart; child < childrenEnd; ++child) {
if (arr[child] > arr[biggest]) {
biggest = child;
}
}
if (biggest != i) {
swap(arr[i], arr[biggest]);
heapify(arr, n, d, biggest);
}
}
template <typename T>
void heapsort(T arr[], int n, int d)
{
if (n<=0) {
return;
}
for (int i = (n-2) / d; i >= 0; i--) {
heapify(arr, n, d, i);
}
for (int i = n - 1; i > 0; i--) {
swap(arr[0], arr[i]);
heapify(arr, i, d, 0);
}
}

C++ creating a Templatized function to sort both int and strings

I'm working with Template Functions and am looking to create one that sorts both ints and strings. I have managed to get it working with ints but having trouble with strings.
Here is my code so far.
#include <iostream>
#include <string>
using namespace std;
template <class T>
void print(T arr[], int n);
template <class T>
void swap(T arr[], int i, int j);
template <class T>
T getSmallest(T arr[], int start, int end);
template <class T>
void selectionSort(T arr[], int n);
int main()
{
int n = 10;
int iarr[] = { 3,5,9,2,1,7,8,4,0,6 };
selectionSort<int>(iarr, n);
print<int>(iarr, n);
cout << endl;
string sarr[] = { "skunk", "hedgehog", "aardvark", "zebra", "rat", "cat", "hippopotamus", "hamster", "manatee", "red panda" };
selectionSort<string>(sarr, n);
print<string>(sarr, n);
cout << endl;
return 0;
}
// PRE: length of arr = n
// PARAM: arr = array of integers of length n
// POST: prints arr[0:n]
template <class T>
void print(T arr[], int n) {
for (int i = 0; i < n; i++) {
cout << arr[i] << endl;
}
}
// PRE: i, j < length of arr
// PARAM: arr = array of integers of length n, i and j are indexes
// POST: swaps arr[i] with arr[j]
template <class T>
void swap(T arr[], int i, int j) {
T temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
// PRE: 0 <= start < end <= length of arr
// PARAM: arr = array of integers
// start = start index of sub-array
// end = end index of sub-array + 1
// POST: returns index of smallest value in arr{start:end}
template <class T>
T getSmallest(T arr[], int start, int end) {
T smallest = start;
for (int i = start + 1; i < end; ++i) {
if (arr[i] < arr[smallest]) {
smallest = i;
}
}
return smallest;
}
// PRE: length of arr = n
// PARAM: arr = array of integers of length n
// POST: sorts arr in ascending order
template <class T>
void selectionSort(T arr[], int n) {
for (int i = 0; i < n - 1; ++i) {
int smallest = getSmallest(arr, i, n);
swap(arr, i, smallest);
}
}
I know I need to find some way to make "smallest" somehow able to work for strings. But simply turning it from int to template T isn't working. Any suggestions?
You almost have it.
The only thing off is that getSmallest() returns an index regardless of what T is. So that part should not be templated.
template <class T>
int getSmallest(T arr[], int start, int end) {
int smallest = start;
This function
template <class T>
T getSmallest(T arr[], int start, int end) {
T smallest = start;
for (int i = start + 1; i < end; ++i) {
if (arr[i] < arr[smallest]) {
smallest = i;
}
}
return smallest;
}
has an invalid return type. For an array of objects of the type std::string it will have the return type std::string.
It should look like
template <class T>
int getSmallest(T arr[], int start, int end) {
int smallest = start;
for (int i = start + 1; i < end; ++i) {
if (arr[i] < arr[smallest]) {
smallest = i;
}
}
return smallest;
}
Do not forget to change also the function template declaration
template <class T>
int getSmallest(T arr[], int start, int end)
You changed to many int to T.
template <class T>
T getSmallest(T arr[], int start, int end) {
T smallest = start;
for (int i = start + 1; i < end; ++i) {
if (arr[i] < arr[smallest]) {
smallest = i;
}
}
return smallest;
}
If you use int for the index then smallest should be int and the return type should be int, not T. Actually I suggest to use size_t for indices (unless you want to pass iterators):
template <class T>
size_t getSmallest(T arr[], size_t start, size_t end) {
size_t smallest = start;
for (size_t i = start + 1; i < end; ++i) {
if (arr[i] < arr[smallest]) {
smallest = i;
}
}
return smallest;
}

Call a function pointer in a function C++

In main function I calls the Choice function. The goal is to choose the sort type with the order of increase or decrease.
I calls Choice to receive the address of two IsBigger and Interchange functions.
Here is the code
#include <iostream>
using namespace std;
template <class T>
void Swap(T *a, T *b)
{
T temp = *a;
*a = *b;
*b = temp;
}
template <class T>
void Interchange(T a[], int n, bool(*Cmp)(T, T))
{
for (int i = 0; i < n - 1; i++)
for (int j = i + 1; j < n; j++)
if (Cmp(a[i], a[j]))
Swap(&a[i], &a[j]);
}
template <class T>
void Insertion(T a[], int n, bool(*Cmp)(T, T))
{
int x, j;
for (int i = 0; i < n; i++)
{
x = a[i]; j = i - 1;
while (j >= 0 && !Cmp(a[j],x))
{
a[j + 1] = a[j];
j--;
}
a[j + 1] = x;
}
}
template <class T>
bool IsBigger(T a, T b)
{
return a > b;
}
template <class T>
void Choice(T a[], int n, bool(*Cmp)(T, T), void(*Sort)(T*, int, bool*))
{
Sort(a, n, Cmp);
}
int main()
{
int a[] = { 3,4,1,5 };
Choice<int>(a, 4, &IsBigger, &Interchange);
return 0;
}
In main function, function Choice has error when it calls IsBigger and Interchange. The error is "no instance of function template "Choice" matches the argument list"
I don't know what am I wrong. Please help me. Thanks a lot

Keep track of swaps and comparisons

I am writing a sorting program that needs to take a an array, fill it with 1000 random numbers, then copy the array into a second array. After that the program is supposed to use the selectionSort function and the insertSort function .
But when I use the functions I am supposed to keep track of all the swaps and key comparisons. I have figured out how to do that for the insertSort. I cannot figure out how to do this for the selectionSort
Here is my code:
template <class elemType>
void selectionSort(elemType list[], int length)
{
int loc, minIndex;
int swaps =0;
for (loc = 0; loc < length; loc++)
{
minIndex = minLocation(list, loc, length - 1);
swap(list, loc, minIndex);
}
cout<<"swaps = "<<swaps<<endl;
} //end selectionSort
template <class elemType>
void swap(elemType list[], int first, int second)
{
elemType temp;
temp = list[first];
list[first] = list[second];
list[second] = temp;
} //end swap
template <class elemType>
int minLocation(elemType list[], int first, int last)
{
int loc, minIndex;
minIndex = first;
for (loc = first + 1; loc <= last; loc++)
{ if (list[loc] < list[minIndex])
minIndex = loc;
}
return minIndex;
} //end minLocation
template <class elemType>
void insertionSort(elemType list[], int length)
{
int swaps = 0;
int comp = 0;
for (int firstOutOfOrder = 1; firstOutOfOrder < length;
firstOutOfOrder++)
if (list[firstOutOfOrder] < list[firstOutOfOrder - 1])
{
elemType temp = list[firstOutOfOrder];
int location = firstOutOfOrder;
do
{
list[location] = list[location - 1];
location--;
comp +=1;
} while(location > 0 && list[location - 1] > temp);
list[location] = temp;
swaps +=1;
}
cout<<"swaps = "<<swaps<<endl;
cout<<"comps = "<<comp<<endl;
} //end insertionSort
#include<iostream>
#include <ctime>
#include <cstdlib>
#include "searchSortAlgorithms.h"
using namespace std;
int main (){
//generate a new random set each time
srand(time(0));
int a[1000] = {0};
int b[1000] = {0};
for(int i= 0; i<1000; i++)
{
a[i] = rand()% 1000;
b[i] = a[i];
}
insertionSort(a, 1000);
selectionSort(b, 1000);
return 0;
}
The swaps and comparisons print out for the inertionSort, but I am unfamiliar with how I would get it to work with the selectionSort since the sort algorithm calls other functions in a for loop.
Any input would be appreciated it.
Actually, your selection sort has a fixed number of comparations (length * (length-1) / 2) and swaps (length).
If you want to count them anyway,
// Count variables can be defined in main(), and passed through
// to swap(), minLocation().
template <class elemType>
void swap(elemType list[], int first, int second)
{
// Count swap here
}
template <class elemType>
int minLocation(elemType list[], int first, int last)
{
..
for (loc = first + 1; loc <= last; loc++)
{
// Count comparation here
}
..
}
By the way, your counting of comparations in insertion sort is not complete either.
for (int firstOutOfOrder = 1; firstOutOfOrder < length; firstOutOfOrder++)
{
// You miss the count here.
if (list[firstOutOfOrder] < list[firstOutOfOrder - 1])

When does merge_sort beat quick_sort?

SO Posts
When to use merge sort and when to use quick sort?
Quick Sort Vs Merge Sort
Wikipedia
http://en.wikipedia.org/wiki/Merge_sort
http://en.wikipedia.org/wiki/Quicksort
quick_sort is suppose to have worst case O(n^2) but merge_sort is suppose to not have a worst case and always be O (n*log N). I thought that it was dependent upon the ordering of the data set - reverse order, forward order, or random, but when I a run test...quick_sort is always faster. The code I used is below:
/*
Needs a reszie function added
*/
#include "c_arclib.cpp"
template <class T> class dynamic_array
{
private:
T* array;
T* scratch;
public:
int size;
dynamic_array(int sizein)
{
size=sizein;
array = new T[size]();
}
void print_array()
{
for (int i = 0; i < size; i++) cout << array[i] << endl;
}
void merge_recurse(int left, int right)
{
if(right == left + 1)
{
return;
}
else
{
int i = 0;
int length = right - left;
int midpoint_distance = length/2;
int l = left, r = left + midpoint_distance;
merge_recurse(left, left + midpoint_distance);
merge_recurse(left + midpoint_distance, right);
for(i = 0; i < length; i++)
{
if((l < (left + midpoint_distance)) && (r == right || array[l] > array[r]))
{
scratch[i] = array[l];
l++;
}
else
{
scratch[i] = array[r];
r++;
}
}
for(i = left; i < right; i++)
{
array[i] = scratch[i - left];
}
}
}
int merge_sort()
{
scratch = new T[size]();
if(scratch != NULL)
{
merge_recurse(0, size);
return 1;
}
else
{
return 0;
}
}
void quick_recurse(int left, int right)
{
int l = left, r = right, tmp;
int pivot = array[(left + right) / 2];
while (l <= r)
{
while (array[l] < pivot)l++;
while (array[r] > pivot)r--;
if (l <= r)
{
tmp = array[l];
array[l] = array[r];
array[r] = tmp;
l++;
r--;
}
}
if (left < r)quick_recurse(left, r);
if (l < right)quick_recurse(l, right);
}
void quick_sort()
{
quick_recurse(0,size);
}
void rand_to_array()
{
srand(time(NULL));
int* k;
for (k = array; k != array + size; ++k)
{
*k=rand();
}
}
void order_to_array()
{
int* k;
int i = 0;
for (k = array; k != array + size; ++k)
{
*k=i;
++i;
}
}
void rorder_to_array()
{
int* k;
int i = size;
for (k = array; k != array + size; ++k)
{
*k=i;
--i;
}
}
};
int main()
{
dynamic_array<int> d1(1000000);
d1.order_to_array();
clock_t time_start=clock();
d1.merge_sort();
clock_t time_end=clock();
double result = (double)(time_end - time_start) / CLOCKS_PER_SEC;
cout << result;
}
Worst case for quick sort is when the pivot element is the largest or smallest element in the array on every recursion. In that case you will have to do n-1 recursions (one of the arrays you split always only has one element) which gives you an O(n2) overall.
You can reproduce the worst case for quick sort if you use an already sorted array and pick the first or last element as pivot element.
Merge sort works very well for data that won't fit into memory, because each pass is linear and can be read/written to disk. Quick sort isn't even an option in that case, although the two may be combined - quick sort blocks that fit into memory, and merge sort those blocks until done.
Consider the container type as well - mergesort will work much better with a linked list, because you can split the list into equal parts by just traversing it and assigning nodes to alternate sublists; rearranging things around a pivot for quicksort is considerably more involved.