c++ quick sort comparison query - c++

For some reason it's taken me a while to get my head around this one, but there's one bit I'm still not sure about.
In the code below, this bit:
a.at(first) = a.at(last);
a.at(last) = a.at(++first);
a.at(first) = pivot;
My question is, when a.at(++first) is swapped with a.at(last), it hasn't been compared, has it? a.at(last) is lower that the pivot, so is being moved, but there is no way of knowing if a.at(++first) is greater than or equal to pivot, is there? Or am I missing something?
#include <iostream>
#include <vector>
using namespace std;
void quick(vector<int>&);
void quick_helper(vector<int>&, int, int);
void print(vector<int>);
int main(){
vector<int>v(10);
v.at(0) = 8;
v.at(1) = 3;
v.at(2) = 7;
v.at(3) = 2;
v.at(4) = 5;
v.at(5) = 9;
v.at(6) = 1;
v.at(7) = 4;
v.at(8) = 0;
v.at(9) = 6;
cout << "Before sorting:\n";
print(v);
quick (v);
cout << "After sorting:\n";
print(v);
return 0;
}
void print(vector<int> a)
{
for (int i = 0; i < static_cast<int>(a.size()); i++)
cout << a[i] << " ";
cout << "\n";
}
void quick(vector<int>& a){
quick_helper(a, 0, static_cast<int>(a.size() - 1));
}
void quick_helper(vector<int>& a, int l, int r){
int i, first, last, pivot;
if (r>l){
first = l;
last = r;
i = (l+r)/2;
pivot = a.at(i);
a.at(i) = a.at(first);
a.at(first) = pivot;
while (last > first){
if (a.at(last) >= pivot){
last--;
} else {
a.at(first) = a.at(last);
a.at(last) = a.at(++first);
a.at(first) = pivot;
}
}
pivot = first;
quick_helper(a, l, pivot-1);
quick_helper(a, pivot+1, r);
}
return;
}

there is no way of knowing if a.at(++first) is greater than or equal to pivot
Yes you are right, a.at(++first) is just an unknown value that takes place of the swapped one. It will be compared on the next while-loop iteration in if (a.at(last) >= pivot) last--.
It works because your pivot is always at first position.

Related

Passing Swaps and Comparisons into quicksort function

I'm making this program where I have to count the number of swaps and comparisons a quick sort function, and we have to pass the swaps and comps to the function. I'm not too sure how to do this. I have it so it can be done without passing anything to it, as shown below.
#include <iostream>
#include <ctime>
#include <stdlib.h>
#include <math.h>
using namespace std;
struct SwapandComp {
int swaps;
int comps;
};
const long ARRAY_SIZE = 5000;
int totalSwaps = 0;
int totalComps = 0;
int partition(int[], int, int) //add parameters int& swap and int& comp
SwapandComp quickSort(int[], int, int) //also add parameters for int& swap and int& comp
int main() {
SwapandComp qui;
long masterAry[ARRAY_SIZE] = {0};
int quickAry[ARRAY_SIZE] = {0};
int start = 0;
int end = 0;
double difference = 0;
int size = ARRAY_SIZE;
srand(time(NULL));
for (int i = 0; i < ARRAY_SIZE; i++) {
masterAry[i] = rand();
}
for (int a = 0; a < ARRAY_SIZE; a++) {
quickAry[a] = masterAry[a];
}
start = clock();
qui = quickSort(quickAry, 0, ARRAY_SIZE - 1);
end = clock();
difference = end - start;
double f = difference / CLOCKS_PER_SEC;
cout << "Quick: " << f << " " << qui.swaps << " " << qui.comps << endl;
}
This is the main. It's where values are assigned to the array to be sorted by the quickSort function, which will be defined below.
int partition(int numbers[], int i, int k) { //add parameters int& swap and int& comp
int l = 0;
int h = 0;
int midpoint = 0;
int pivot = 0;
int temp = 0;
bool done = false;
// Pick middle element as pivot
midpoint = i + (k - i) / 2;
pivot = numbers[midpoint];
l = i;
h = k;
while (!done) {
// Increment l while numbers[l] < pivot
while (numbers[l] < pivot) {
++l;
totalComps++;
}
// Decrement h while pivot < numbers[h]
while (pivot < numbers[h]) {
--h;
totalComps++;
}
// If there are zero or one elements remaining,
// all numbers are partitioned. Return h
if (l >= h) {
totalComps++;
done = true;
}
else {
// Swap numbers[l] and numbers[h],
// update l and h
temp = numbers[l];
numbers[l] = numbers[h];
numbers[h] = temp;
totalSwaps++;
++l;
--h;
}
}
//cout << totalSwaps << " " << totalComps << endl;
return h;
}
This is the partition function to find where to find the next partition point
SwapandComp quickSort(int numbers[], int i, int k) { //add parameters int& swap and int& comp
SwapandComp quick = { 0 };
//quick.swaps = quick.comps = 0;
int j = 0;
int z = 0;
// Base case: If there are 1 or zero elements to sort,
// partition is already sorted
if (i >= k) {
return quick;
}
// Partition the data within the array. Value j returned
// from partitioning is location of last element in low partition.
j = partition(numbers, i, k);
// Recursively sort low partition (i to j) and
// high partition (j + 1 to k)
quickSort(numbers, i, j);
quickSort(numbers, j + 1, k);
quick.swaps = totalSwaps;
quick.comps = totalComps;
//totalSwaps = 0;
//totalComps = 0;
return quick;
}
And finally, here is the quick sort function where all the swaps and comps will be added together and put into the struct. Again, I'm not too sure how to add in the pass by reference variables for swap and comp. Any help is appreciated! (Also sorry about the code formatting, it got kind of crazy on my screen.)

Calling function from one class in another in C++

So, I am writing a program in C++ that has a function in the Sort class that I wish to call in the Main class. The function has several data members used that are present in that class, that are not present in the Main class and I keep getting a C2660 error, that "Function does not take 0 arguments". Is there a way (short of writing a bunch of getters and setters) to resolve this?
#include "Sort.h"
#include "Timer.h"
using namespace std;
int main
{
Sort *sort = new Sort();
Timer ti;
sort->SetRandomSeed(12345);
sort->InitArray();
cout << "starting InsertionSort" << endl;
ti.Start();
sort->InsertionSort();
ti.End();
cout << "Insertion sort duration: " << ti.DurationInMilliSeconds() << "ms" << endl;
//sort->InitList();
//cout << "starting InsertionSortList()" << endl;
//ti.Start();
//sort->InsertionSortList();
//ti.End();
//cout << "Insertion sort list duration: " << ti.DurationInMilliSeconds() << "ms" << endl;
sort->InitArray();
cout << "starting SelectionSort" << endl;
ti.Start();
sort->SelectionSort();
ti.End();
cout << "SelectionSort duration: " << ti.DurationInMilliSeconds() << "ms" << endl;
sort->InitArray();
cout << "starting MergeSort" << endl;
ti.Start();
sort->MergeSort();
ti.End();
cout << "MergeSort duration: " << ti.DurationInMilliSeconds() << "ms" << endl;
sort->InitArray();
cout << "starting QuickSort" << endl;
ti.Start();
sort->QuickSort();
ti.End();
cout << "QuickSort duration: " << ti.DurationInMilliSeconds() << "ms" << endl;
sort->InitVector();
cout << "starting std::sort() of Vector<int>" << endl;
ti.Start();
sort->VectorSort();
ti.End();
cout << "std::sort() duration: " << ti.DurationInNanoSeconds() << "ns" << endl;
delete sort;
cout << endl <<"Press [Enter] key to exit";
getchar();
}
Sort.cpp
//const int for array
int num = 10000000;
int val = 10000;
//array
int *tmpArray, *qArr, *insArr, *selArr, *mergArr = NULL;
int low, high;
//duration for timer
int duration = 0;
Sort::Sort()
{
}
Sort::~Sort()
{
}
void Sort::InitArray()
{
//int for index
int i = 0;
tmpArray = new int[num];
qArr = new int[num];
insArr = new int[num];
selArr = new int[num];
mergArr = new int[num];
//fill temp array with sequential numbers
for (int i = 0; i < num; i++)
{
tmpArray[i] = 1 + rand() % val;
}
for (i = 0; i < num; i++)
{
qArr[i] = tmpArray[i];
insArr[i] = tmpArray[i];
selArr[i] = tmpArray[i];
mergArr[i] = tmpArray[i];
}
low = qArr[0];
high = qArr[num - 1];
int n = sizeof(tmpArray) / sizeof(tmpArray[0]);
}
void Sort::InitVector()
{
vector<int> v(num);
std::generate(v.begin(), v.end(), std::rand);
}
void Sort::InitList()
{
// A set to store values
std::list<int> l;
// Loop until we get 50 unique random values
while (l.size() < num)
{
l.push_back(1 + rand() % val);
}
for (int n : l) {
std::cout << n << '\n';
}
}
//setting seed
void Sort::SetRandomSeed(unsigned int seed)
{
seed = rand();
}
void Sort::InsertionSort()
{
int i, key, j;
for (i = 1; i < n; i++)
{
key = insArr[i];
j = i - 1;
/* Move elements of arr[0..i-1], that are
greater than key, to one position ahead
of their current position */
while (j >= 0 && insArr[j] > key)
{
insArr[j + 1] = insArr[j];
j = j - 1;
}
insArr[j + 1] = key;
}
delete[] insArr;
insArr = NULL;
}
int Sort::partition(int qArr[], int low, int high)
{
int pivot = qArr[high]; // pivot
int i = (low - 1); // Index of smaller element
for (int j = low; j <= high - 1; j++)
{
// If current element is smaller than or
// equal to pivot
if (qArr[j] <= pivot)
{
i++; // increment index of smaller element
swap(&qArr[i], &qArr[j]);
}
}
swap(&qArr[i + 1], &qArr[high]);
return (i + 1);
}
void Sort::QuickSort(int qArr[], int low, int high)
{
if (low < high)
{
/* pi is partitioning index, arr[p] is now
at right place */
int pi = partition(qArr, low, high);
// Separately sort elements before
// partition and after partition
QuickSort(qArr, low, pi - 1);
QuickSort(qArr, pi + 1, high);
}
delete[] qArr;
qArr = NULL;
}
void Sort::SelectionSort()
{
int i, j, min_idx;
// One by one move boundary of unsorted subarray
for (i = 0; i < n - 1; i++)
{
// Find the minimum element in unsorted array
min_idx = i;
for (j = i + 1; j < n; j++)
if (selArr[j] < selArr[min_idx])
min_idx = j;
// Swap the found minimum element with the first element
swap(&selArr[min_idx], &selArr[i]);
}
delete[] selArr;
selArr = NULL;
}
void Sort::swap(int *xp, int *yp)
{
int temp = *xp;
*xp = *yp;
*yp = temp;
}
void Sort::VectorSort()
{
std::sort(v.begin(), v.end());
}
/* l is for left index and r is right index of the
sub-array of arr to be sorted */
void Sort::merge(int mergArr[], int l, int m, int r)
{
int i, j, k;
int n1 = m - l + 1;
int n2 = r - m;
int* L;
int* R;
/* create temp arrays */
L = new int[n1];
R = new int[n2];
/* Copy data to temp arrays L[] and R[] */
for (i = 0; i < n1; i++)
L[i] = mergArr[l + i];
for (j = 0; j < n2; j++)
R[j] = mergArr[m + 1 + j];
/* Merge the temp arrays back into arr[l..r]*/
i = 0; // Initial index of first subarray
j = 0; // Initial index of second subarray
k = l; // Initial index of merged subarray
while (i < n1 && j < n2)
{
if (L[i] <= R[j])
{
mergArr[k] = L[i];
i++;
}
else
{
mergArr[k] = R[j];
j++;
}
k++;
}
/* Copy the remaining elements of L[], if there
are any */
while (i < n1)
{
mergArr[k] = L[i];
i++;
k++;
}
/* Copy the remaining elements of R[], if there
are any */
while (j < n2)
{
mergArr[k] = R[j];
j++;
k++;
}
}
void Sort::MergeSort(int mergArr[], int l, int r)
{
if (l < r)
{
// Same as (l+r)/2, but avoids overflow for
// large l and h
int m = l + (r - l) / 2;
// Sort first and second halves
MergeSort(mergArr, l, m);
MergeSort(mergArr, m + 1, r);
merge(mergArr, l, m, r);
}
delete[] mergArr;
mergArr = NULL;
}
Sort.h
#include <iomanip>
#include <fstream>
#include <string>
#include <queue>
#include <stack>
#include <vector>
#include<iostream>
#include<cstdio>
#include<sstream>
#include<algorithm>
#include<list>
using namespace std;
#pragma once
class Sort
{
public:
Sort();
~Sort();
void InitArray();
void InitVector();
void InitList();
void SetRandomSeed(unsigned int seed);
int n, right, left, l, r, m;
vector<int> v;
void InsertionSort();
int partition(int qArr[], int low, int high);
void QuickSort(int qArr[], int low, int high);
void swap(int * xp, int * yp);
void VectorSort();
void MergeSort(int arr[], int l, int r);
void merge(int arr[], int l, int m, int r);
void SelectionSort();
};
Ignoring the Timer class (those are all good) here is the rest of the code. The C2660 errors are shown for the sort->MergeSort() and sort->QuickSort() calls in main.
I resolved the issues myself. I created helper functions in the Sort class that have no arguments and call the functions themselves to use in the main. Helper Functions shown below.
Sort.cpp
//method for Main to run to prevent C2660 errors
void Sort::mergeHelper()
{
MergeSort(mergArr, l, r);//call merge sort method
}
//method for Main to run to prevent C2660 errors
void Sort::quickHelper()
{
QuickSort(qArr, low, high);//call quick sort method
}
int Main
{
sort->quickHelper();
sort->mergeHelper();
}

C++ Quicksort index 0 returns -842150451

Basic quicksort function that partitions and has myswap. everything works fine except for index 0 in the array.
#include<iostream>
#include<string>
#include<iostream>
#include<vector>
#include <ctime>
using namespace std;
//swap
void myswap(int mya[], int a, int b) {
int temp = mya[a];
mya[a] = mya[b];
mya[b] = temp;
}
//partition, returns pivot index
int mypartition(int mya[], int first, int last)
{
int middle = ((first + last) / 2);
int pivot = mya[middle];
//swap first with middle
myswap(mya, first, middle);
//two pointers
int pivotindex = first;
//loop through the elements
for (int index = first + 1; index <= last; index++) {
if (mya[index] <= pivot)
{
pivotindex++;
myswap(mya, pivotindex, index);
}
}
//swap the pivot in its right place
myswap(mya, first, pivotindex);
return pivotindex;
}
void QuickSort(int mya[], int a, int b)
{
//partition
if (a <= b)
{
int index = mypartition(mya, a, b);
QuickSort(mya, a, index - 1);
QuickSort(mya, index + 1, b);
}
}
int main() {
//vector<int> mya;
int * mya = new int[5000000];
srand(time(0));
int i = 0;
int last = 0;
while(i < 100)
{
int x = (rand() + time(0)) % 5000000;
mya[last] = x;
last++;
i++;
}
clock_t startTime, endTime;
startTime = clock();
QuickSort(mya, 0, last);
endTime = clock();
cout << "Sorted in " << (double)(endTime - startTime) / CLOCKS_PER_SEC << " seconds" << endl;
for (int i = 0; i < 100; i++)
{
cout << mya[i] << endl;
}
delete[] mya;
return 0;
}
The problem im having is that the array gets sorted but when mya[0] is called in the for loop it outputs -842150451. This is just a basic quicksort and for some reason im having trouble with it.
You are calling it wrong.
QuickSort(mya, 0, last-1);
Remember that there are last elements, meaning they are indexed 0..last-1.
You do also have a potential overflow problem with your calculation of middle. Use (last - first + 1)/2 + first.
Hope this helps.
Integer Overflow Causing this problem.
int x = (rand() + time(0)) % 5000000;
this line sometime two 10 digits long numbers whose sum is causing the integer overflow.
Just modify that statement as following and your code starts working:
int x = (rand() % 5000000) + (time(0) % 5000000);
Edit: It was a problem I found executing your code using Ideone. Further noticing I found Your Index 0 problem is actually caused by partition function.
change for (int index = first + 1; index <= last; index++) { this line to
for (int index = first + 1; index < last; index++) { //remove the equal sign
N.B: for me this fixed your issue. But I think in your void QuickSort(int mya[], int a, int b)
if (a <= b) should be changed to if (a < b).

My quickselect algorithm is not returning the correct value

So I'm trying to implement a quickselect algorithm in C++ in order to find the median value in a vector, however it is not properly partially-sorting the list and is also not returning the correct median value.
I can't seem to find where the error is. I'm new to this algorithm and it's my first time trying to implement it. I've included my code below so if anyone more knowledgable than me has any idea on what is going wrong, I would very much appreciate your input.
//Returns the index of the object with the kth largest value
int QuickSelect(vector<Object *> & list, int left, int right, int k){
/*-Base case-*/
if(left == right) /*List only contains a single element*/
return left; /*Return that index*/
int pivotIndex = left + (rand() % (int)(right - left + 1));
int pivotNewIndex = Partition(list, level, left, right, pivotIndex);
int pivotDist = pivotNewIndex - left + 1;
if(pivotDist == k)
return pivotNewIndex;
else if (k < pivotDist)
return QuickSelect(list, level, left, pivotNewIndex-1, k);
else
return QuickSelect(list, level, pivotNewIndex+1, right, k-pivotDist);
}
int Partition(vector<Object *> & list, int left, int right, int pivotIndex){
int pivotValue = list.at(pivotIndex)->value;
std::swap(list[pivotIndex], list[right]);
int storeIndex = left;
for(int i = left; i < right; i++){
if(list.at(i)->value < pivotValue){
std::swap(list[storeIndex], list[i]);
storeIndex++;
}
}
std::swap(list[right], list[storeIndex]);
return storeIndex;
}
int pivotDist = pivotNewIndex - left + 1;
should be
int pivotDist = pivotNewIndex - left;
Also
return QuickSelect(list, pivotNewIndex+1, right, k-pivotDist);
should be
return QuickSelect(list, pivotNewIndex+1, right, k-pivotDist-1);
My test code was:
int main() {
int d[] = {0, 1, 2, 3, 4};
do {
std::vector<Object*> v;
v.push_back(new Object(d[0]));
v.push_back(new Object(d[1]));
v.push_back(new Object(d[2]));
v.push_back(new Object(d[3]));
v.push_back(new Object(d[4]));
for (int i = 0; i < v.size(); ++i) {
std::cout << v[i]->value << " "; }
std::cout << std::endl;
int n = QuickSelect(v, 0, 4, 2);
if (v[n]->value != 2) {
std::cout << "error: ";
for (int i = 0; i < v.size(); ++i) {
std::cout << v[i]->value << " "; }
std::cout << std::endl;
}
}
while (std::next_permutation(&d[0], &d[sizeof(d)/sizeof(int)]));
}

Algorithm to compute mode

I'm trying to devise an algorithm in the form of a function that accepts two parameters, an array and the size of the array. I want it to return the mode of the array and if there are multiple modes, return their average. My strategy was to take the array and first sort it. Then count all the occurrences of a number. while that number is occurring, add one to counter and store that count in an array m. So m is holding all the counts and another array q is holding the last value we were comparing.
For example: is my list is {1, 1, 1, 1, 2, 2, 2}
then i would have m[0] = 4 q[0] = 1
and then m[1] = 3 and q[1] = 2.
so the mode is q[0] = 1;
unfortunately i have had no success thus far. hoping someone could help.
float mode(int x[],int n)
{
//Copy array and sort it
int y[n], temp, k = 0, counter = 0, m[n], q[n];
for(int i = 0; i < n; i++)
y[i] = x[i];
for(int pass = 0; pass < n - 1; pass++)
for(int pos = 0; pos < n; pos++)
if(y[pass] > y[pos]) {
temp = y[pass];
y[pass] = y[pos];
y[pos] = temp;
}
for(int i = 0; i < n;){
for(int j = 0; j < n; j++){
while(y[i] == y[j]) {
counter++;
i++;
}
}
m[k] = counter;
q[k] = y[i];
i--; //i should be 1 less since it is referring to an array subscript
k++;
counter = 0;
}
}
Even though you have some good answers already, I decided to post another. I'm not sure it really adds a lot that's new, but I'm not at all sure it doesn't either. If nothing else, I'm pretty sure it uses more standard headers than any of the other answers. :-)
#include <vector>
#include <algorithm>
#include <unordered_map>
#include <map>
#include <iostream>
#include <utility>
#include <functional>
#include <numeric>
int main() {
std::vector<int> inputs{ 1, 1, 1, 1, 2, 2, 2 };
std::unordered_map<int, size_t> counts;
for (int i : inputs)
++counts[i];
std::multimap<size_t, int, std::greater<size_t> > inv;
for (auto p : counts)
inv.insert(std::make_pair(p.second, p.first));
auto e = inv.upper_bound(inv.begin()->first);
double sum = std::accumulate(inv.begin(),
e,
0.0,
[](double a, std::pair<size_t, int> const &b) {return a + b.second; });
std::cout << sum / std::distance(inv.begin(), e);
}
Compared to #Dietmar's answer, this should be faster if you have a lot of repetition in the numbers, but his will probably be faster if the numbers are mostly unique.
Based on the comment, it seems you need to find the values which occur most often and if there are multiple values occurring the same amount of times, you need to produce the average of these. It seems, this can easily be done by std::sort() following by a traversal finding where values change and keeping a few running counts:
template <int Size>
double mode(int const (&x)[Size]) {
std::vector<int> tmp(x, x + Size);
std::sort(tmp.begin(), tmp.end());
int size(0); // size of the largest set so far
int count(0); // number of largest sets
double sum(0); // sum of largest sets
for (auto it(tmp.begin()); it != tmp.end(); ) {
auto end(std::upper_bound(it, tmp.end(), *it));
if (size == std::distance(it, end)) {
sum += *it;
++count;
}
else if (size < std::distance(it, end)) {
size = std::distance(it, end);
sum = *it;
count = 1;
}
it = end;
}
return sum / count;
}
If you simply wish to count the number of occurences then I suggest you use a std::map or std::unordered_map.
If you're mapping a counter to each distinct value then it's an easy task to count occurences using std::map as each key can only be inserted once. To list the distinct numbers in your list simply iterate over the map.
Here's an example of how you could do it:
#include <cstddef>
#include <map>
#include <algorithm>
#include <iostream>
std::map<int, int> getOccurences(const int arr[], const std::size_t len) {
std::map<int, int> m;
for (std::size_t i = 0; i != len; ++i) {
m[arr[i]]++;
}
return m;
}
int main() {
int list[7]{1, 1, 1, 1, 2, 2, 2};
auto occurences = getOccurences(list, 7);
for (auto e : occurences) {
std::cout << "Number " << e.first << " occurs ";
std::cout << e.second << " times" << std::endl;
}
auto average = std::accumulate(std::begin(list), std::end(list), 0.0) / 7;
std::cout << "Average is " << average << std::endl;
}
Output:
Number 1 occurs 4 times
Number 2 occurs 3 times
Average is 1.42857
Here's a working version of your code. m stores the values in the array and q stores their counts. At the end it runs through all the values to get the maximal count, the sum of the modes, and the number of distinct modes.
float mode(int x[],int n)
{
//Copy array and sort it
int y[n], temp, j = 0, k = 0, m[n], q[n];
for(int i = 0; i < n; i++)
y[i] = x[i];
for(int pass = 0; pass < n - 1; pass++)
for(int pos = 0; pos < n; pos++)
if(y[pass] > y[pos]) {
temp = y[pass];
y[pass] = y[pos];
y[pos] = temp;
}
for(int i = 0; i < n;){
j = i;
while (y[j] == y[i]) {
j++;
}
m[k] = y[i];
q[k] = j - i;
k++;
i = j;
}
int max = 0;
int modes_count = 0;
int modes_sum = 0;
for (int i=0; i < k; i++) {
if (q[i] > max) {
max = q[i];
modes_count = 1;
modes_sum = m[i];
} else if (q[i] == max) {
modes_count += 1;
modes_sum += m[i];
}
}
return modes_sum / modes_count;
}