I was trying to implement quick sort now. But, i have some problem with the loop part below
for (int current = 0; current <= high - 1; current++)
When i initialize that 'current' statement with 0, it show nothing on the screen when i run it. Then, i try to replace it with 'low' argument like in the implementation provided and it was run appropriately.
What i want to ask is, why it doesn't work when i initialize the loop statement with 0, which was the same value assigned to 'low' parameter? I have tried initialize new variable with 0 and use that variable to the loop, but it give the same result like when i directly assign the loop statement with 0. Thanks for the answer.
Here the code:
int partition(int arr[], int low, int high)
{
int pivot = arr[high];
int index = (low - 1);
for (int current = low; current <= high - 1; current++)
{
if (arr[current] <= pivot)
{
index++;
swap(arr[index], arr[current]);
}
}
swap(arr[index+1], arr[high]);
return (index + 1);
}
void quicksort(int arr[], int low, int high)
{
if (low < high)
{
int pi = partition(arr, low, high);
quicksort(arr, low, pi - 1);
quicksort(arr, pi + 1, high);
}
}
int main()
{
int arr[] = { 3, 4, 2, 5, 1 };
int n = sizeof(arr)/sizeof(arr[0]);
quicksort(arr, 0, n-1);
for (int i = 0; i < n; i++)
{
cout << arr[i] << " ";
}
}
The result when i set the 'current' value with 0
low is only 0 in the first call to partition; it takes different values in other calls. Note that when quicksort calls itself the second time, low will get assigned pi + 1.
Print it out in the beginning of partition to observe this for some known not-too-large array - this should be a good educational exercise in general. I mean make the first line of partition:
cout << "low = " << low << "\n";
Related
Here is the pseudocode straight from the book (CORMEN):
Partition(A,p,r)
x=A[p]
i=p-1
j=r+1
while(TRUE)
repeat
j=j-1
until A[j]<=x
repeat
i=i+1
until A[i]>=x
if i<j
SWAP A[i] <=> A[j]
else return j
Here is code in C++:
#include<bits/stdc++.h>
using namespace std;
int partition(int a[], int low, int high)
{
int pivot = a[low];
int i = low - 1;
int j = high + 1;
while (1)
{
do {
i++;
} while (a[i] < pivot);
do {
j--;
} while (a[j] > pivot);
if (i >= j) {
cout<<j<<endl;
return j;
}
swap(a[i], a[j]);
}
}
/* The main function that implements QuickSort
arr[] --> Array to be sorted,
low --> Starting index,
high --> Ending index */
void quickSort(int arr[], int low, int high)
{
if (low < high)
{
/* pi is partitioning index, arr[p] is now
at right place*/
int pi = partition(arr, low, high);
// Separately sort elements before
// partition and after partition
quickSort(arr, low, pi - 1);
quickSort(arr, pi + 1, high);
}
}
/* Function to print an array */
void printArray(int arr[], int size)
{
int i;
for (i=0; i < size; i++)
printf("%d ", arr[i]);
printf("\n");
}
// Driver program to test above functions
int main()
{
int arr[] = {7,3,2,6,4,1,3,5};
int n = sizeof(arr)/sizeof(arr[0]);
cout<<"partition:\n";
partition(arr,0,7);
printArray(arr, n);
quickSort(arr, 0, n-1);
printf("Sorted array: \n");
printArray(arr, n);
return 0;
}
If I use this array in input:
[5,3,2,6,4,1,3,7]
everything works logically well because the array returned by the partitioning will be:
[3,3,2,1,4,6,5,7]
Termination i=5 and j=4 so my pivot is 4. And all elements to the left of 4 are minor and all to the right are major
Now if I use this array in input:
[7,3,2,6,4,1,3,5]
I will have this situation at the end of the partition
[5,3,2,6,4,1,3,7]
which will return to me as pivot j = 6 that is 3. Now the elements on the left of 3 are not all minor and on the right are major.
But how is it possible that this works? Shouldn't I have the elements to the left of the pivot minor and to the right major?
With Hoare partition the pivot and values equal to the pivot can end up anywhere. The returned index is not an index to the pivot, but just a separator. For the code above, when partition is done, then elements <= pivot will be at or to the left of j, and elements >= pivot will be to the right of j. After doing a partition step, the C++ code should be:
quickSort(arr, low, pi); // not pi - 1
quickSort(arr, pi + 1, high);
example code that includes testing of quicksort:
uint32_t Rnd32()
{
static uint32_t r = 0;
r = r*1664525 + 1013904223;
return r;
}
int Partition(int ar[], int lo, int hi)
{
int pv = ar[lo+(hi-lo)/2];
int i = lo - 1;
int j = hi + 1;
while(1){
while(ar[++i] < pv);
while(ar[--j] > pv);
if(i >= j)
return j;
std::swap(ar[i], ar[j]);
}
}
void QuickSort(int ar[], int lo, int hi)
{
while (lo < hi){
int pi = Partition(ar, lo, hi);
if((pi - lo) < (pi - hi)){
QuickSort(ar, lo, pi);
lo = pi + 1;
} else {
QuickSort(ar, pi + 1, hi);
hi = pi;
}
}
}
#define COUNT (16*1024*1024)
int main(int argc, char**argv)
{
size_t i;
int * ar = new int [COUNT];
for(i = 0; i < COUNT; i++){
ar[i] = Rnd32();
}
QuickSort(ar, 0, COUNT-1);
for(i = 1; i < COUNT; i++)
if(ar[i-1] > ar[i])
break;
if(i == COUNT)
std::cout << "passed" << std::endl;
else
std::cout << "failed" << std::endl;
delete[] ar;
return(0);
}
I am trying to sort strings using merge sort and standard string comparison. The problem is, this program is very slow as is. I am exceeding runtime expectations when I am attempting to check my solution. Is there a way I can use some other data structure to do the same thing but be accessed faster? Here is the code. Thank you for your help.
Edit: I understand that using sort() rather than my own merge sort algorithm will be faster, but my requirement states to use merge sort. The problem is exceeding runtime requirements on SPOJ.
#include <iostream>
using namespace std;
void MergeSortA(int low , int high);
void MergeA(int low ,int mid ,int high);
string stringData[50000];
int main()
{
int testCases;
int linesInTestCase;
scanf("%d", &testCases);
while(testCases--)
{
scanf("%d", &linesInTestCase);
for(int i = 0; i < linesInTestCase; ++i)
cin >> stringData[i];
// Sort the data
MergeSortA(0, linesInTestCase - 1);
// Print array of strings one by one
for(int i = 0; i < linesInTestCase; ++i) {
cout << stringData[i] << endl;
}
}
return 0;
}
void MergeSortA(int low , int high)
{
int mid = 0;
if(low < high)
{
mid = ((low+high)/2);
MergeSortA(low , mid);
MergeSortA(mid+1,high);
MergeA(low,mid,high);
}
}
void MergeA(int low ,int mid , int high)
{
int i = low, j = mid+1 , k = low;
string Temp[50000];
while(i <= mid && j <= high) {
if( stringData[i] < stringData[j] ) {
Temp[k].assign(stringData[i]);
i++;
}
else {
Temp[k].assign(stringData[j]);
j++;
}
k++;
}
if(i > mid ) {
for(int h = j ;h <= high ; h++ ) {
Temp[k].assign(stringData[h]);
k++;
}
}
else
for(int h = i; h<= mid ; h++ ) {
Temp[k].assign(stringData[h]);
k++;
}
// Copy from low to high
for(int l = low; l <= high ; l++) {
stringData[l].assign(Temp[l]);
}
}
As you stated that this is SPOJ problem, I will try not alter your structure of your program, However, there is real bottleneck here:
void MergeA(int low ,int mid , int high)
{
int i = low, j = mid+1 , k = low;
string Temp[50000]; //<--- Allocation happens every time this function is called
Wnenever MergeA is called, 50000 strings are constructed & destructed every time, encumbering the algorithm. Just add static there:
void MergeA(int low ,int mid , int high)
{
int i = low, j = mid+1 , k = low;
static string Temp[50000]; //Now it is constructed & destructed only once in the program
We are supposed to compare the speeds of each sort with 10000 inputs. They all work by themselves but when I add them together in the program I think perhaps merge sort takes a lot of space so I always get an unhandled exception: StackOverflow once I reach quicksort. Does anyone know a solution to this problem to perhaps make it so merge sort doesn't take a lot of space (if that is the problem)? Specifically, the exception is thrown at the partition function for quicksort.
#include <iostream>
#include <fstream>
#include <ctime>
using namespace std;
void merge(int arr[], int l, int m, int r) {
int i, j, k;
int n1 = m - l + 1;
int n2 = r - m;
int *L = new int[n1];
int *R = new int[n2];
for (i = 0; i < n1; i++)
L[i] = arr[l + i];
for (j = 0; j < n2; j++)
R[j] = arr[m + 1 + j];
i = 0;
j = 0;
k = l;
while (i < n1 && j < n2) {
if (L[i] <= R[j]) {
arr[k] = L[i];
i++;
} else {
arr[k] = R[j];
j++;
}
k++;
}
while (i < n1) {
arr[k] = L[i];
i++;
k++;
}
while (j < n2) {
arr[k] = R[j];
j++;
k++;
}
}
void mergeSort(int arr[], int l, int r) {
if (l < r) {
int m = l + (r - l) / 2;
mergeSort(arr, l, m);
mergeSort(arr, m + 1, r);
merge(arr, l, m, r);
}
}
int partition(int arr[], int start, int end) { //start is 0 and end is counter-1
int pivot = start; //remember start here
int imp = arr[end];
for (int i = start; i < end; i++) { //remember this is start and end;
if (arr[i] <= imp) {
int temp = arr[i];
arr[i] = arr[pivot];
arr[pivot] = temp;
pivot++;
}
}
int p = arr[pivot];
arr[pivot] = arr[end];
arr[end] = p;
return pivot;
}
void quicksort(int arr[], int start, int end) {
if (start < end) {
int index = partition(arr, start, end);
quicksort(arr, start, index - 1);
quicksort(arr, index + 1, end);
}
}
int main() {
clock_t timereq;
double time_taken;
ifstream in("input3.txt");
int size;
in >> size;
int num;
int *arrq = new int[size];
int i = 0;
while (!in.eof()) {
in >> num;
arrq[i] = num;
i++;
}
timereq = clock();
mergeSort(arrq, 0,size-1);
timereq = clock() - timereq;
time_taken = double(timereq) / CLOCKS_PER_SEC; /// double(CLOCKS_PER_SEC);
cout << time_taken << endl;
timereq = clock();
quicksort(arrq, 0,size-1);
timereq = clock() - timereq;
time_taken = double(timereq) / CLOCKS_PER_SEC; /// double(CLOCKS_PER_SEC);
cout << time_taken << endl;
for (i = 0; i < size; i++) {
cout << arrq[i] << endl;
}
}
The input looks for example like this, the number of values and then the values:
8
3 1 4 1 5 9 2 6
You should really follow the suggestions given in the comments above, and directly tackle the root of the problem (limited stack size) by redesigning your code with stack data structures in place, so to specifically avoid memory-draining recursions.
However, you could also in principle cut corners, and adopt a dirtier and quicker solution: just add flags to your compiler to let it increase the size of the stack.
If you use gcc, you can do this by inserting the -Wl, --stack,<size> keys if compiling from the prompt.
The <size> key above could be any size bigger than your current stack size, -Wl, --stack,1000000000 (9 zeros) for instance.
If you instead are using an IDE, I happen to know how to do this on Codeblocks: go to Settings->Compiler->Global Compiler Settings->Linker Settings-> add the line above under the Other Linker Options field.
See also stackoverflow: How to increase stack size when compiling a C++ program using MinGW compiler
The problem with quicksort is its worse case behavior:
if the partition function does not split the dataset in balanced halves, the complexity can reach O(N2) instead of the average O(N.log(N)).
in your case, the worst case occurs when the list is already sorted: The pivot splits the array into N-1 and 1 parts, causing the recursion to occur N times, probably too much for the default stack depth.
there is a logic error in your benchmark that causes this worst case behavior to occur every time: you measure the time for mergeSort() to sort arrq ad then you do the same for quicksort on the same array, that was just sorted by mergeSort. You should make a copy of the original array and pass that to quicksort, but you must also fix quicksort to avoid this stack overflow.
You can fix this problem by changing the quicksort function to recurse on the smaller half and iterate on the larger one:
void quicksort(int arr[], int start, int end) {
while (start < end) {
int index = partition(arr, start, end);
if (index - start < end - index) {
quicksort(arr, start, index - 1);
start = index + 1;
} else {
quicksort(arr, index + 1, end);
end = index - 1;
}
}
}
This should solve the stack overflow bug, but will not reduce the time complexity. You would need to change the partition function for that, for example by choosing a pivot value at random if the default choice leads to a pathological split.
I am getting stackoverflow error when I am trying to sort using quicksort an array of large size, and this array is in descending order. I want to sort it in ascending order using the code below:
int partition_lastElementPivot(int * arr, int lo, int hi)
{
int x = arr[hi];
int i = lo - 1;
for (int j = lo; j < hi; j++)
{
if (arr[j] <= x)
{
i++;
int temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
}
int temp = arr[hi];
arr[hi] = arr[i + 1];
arr[i + 1] = temp;
return i + 1;
}
void quicksortLastElementPivot(int*arr, int lo, int hi)
{
if (lo<hi)
{
int mid = partition_lastElementPivot(arr, lo, hi);
quicksortLastElementPivot(arr, lo, mid - 1);
quicksortLastElementPivot(arr, mid + 1, hi);
}
}
This code works fine when I randomly generate an array of any size, suppose of size 5000. But when I generate an array of size 5000 sorted in descending order and then try to sort using this code, I get a stackoverflow error. Does C++ limits the memory useable by stack and why is this happening.
int arr[5000];
int count = 5001;
for(int i=0; i<5000; i++)
{
arr[i] = count;
count--;
}
quicksortLastElementPivot(arr, 0, 4999)
Thanks
Quicksort has a truly dreadful worst-case performance, as you have discovered here. It is calling itself at a stack depth of 5000. This Wikipedia article has a good discussion on the subject. In particular, it mentions tail recursion as a solution to your stack overflow problem.
Briefly, this means that instead of the last call to quicksortLastElementPivot, followed immediately by a return, you just loop back to the start of the function. This has the same effect, but the tail recursion doesn't increase the stack size. For this to work, you have to make sure that the smaller of the two partitions is sorted first, using traditional recursion, and the larger partition is sorted by tail recursion. Something like this (not tested!):
void quicksortLastElementPivot(int*arr, int lo, int hi)
{
TailRecurse:
if (lo<hi)
{
int mid = partition_lastElementPivot(arr, lo, hi);
if (mid < (lo + hi) / 2)
{ // First partition is smaller
quicksortLastElementPivot(arr, lo, mid - 1); // Sort first partition
lo = mid + 1; goto TailRecurse; // Sort second partition
}
else
{ // Second partition is smaller
quicksortLastElementPivot(arr, mid + 1, hi); // Sort second partition
hi = mid - 1; goto TailRecurse; // Sort first partition
}
}
}
C++ standard does not define the stack-size of an executable program.
This limit is typically defined in the make file or the linker-command file of your project.
Depending on your IDE, you might also find it within your project settings (under linker-configuration).
The answer given by TonyK is doing quite a good job in explaining the stack usage of quick-sort under the worst-case scenario (which is exactly the case in your code, where arr is sorted in reversed order).
#include <iostream>
using namespace std;
void QuickSort(int *arr, int left, int right)
{
int i = left;
int j = right;
int pivot = arr[rand() % (right - left) + left];
while (i < j)
{
while (arr[i] < pivot)
{
i++;
}
while (arr[j] > pivot)
{
j--;
}
if (i <= j)
{
swap(arr[i], arr[j]);
i++;
j--;
}
}
if (left < j)
{
QuickSort(arr, left, j);
}
if (i < right)
{
QuickSort(arr, i, right);
}
}
int main()
{
freopen("input.txt", "r", stdin);
freopen("output.txt", "w", stdout);
int n;
cin >> n;
int *arr = new int[n];
for (int i = 0; i < n; i++)
{
cin >> arr[i];
}
QuickSort(arr, 0, n - 1);
for (int i = 0; i < n; i++)
{
cout << arr[i] << " ";
}
delete arr;
}
I'm not looking to copy a qsort algorithm. I'm practicing writing qsort and this is what I've come up with and I'm interested in what part of my code is wrong. Please don't tell me that this is homework cause I could just use the code in the link below.
Reference: http://xoax.net/comp/sci/algorithms/Lesson4.php
When this runs I get this in the console:
Program loaded.
run
[Switching to process 10738]
Running…
Current language: auto; currently c++
Program received signal: “EXC_ARITHMETIC”.
void myQSort(int min, int max, int* myArray)
{
// Initially find a random pivot
int pivotIndex = rand() % max;
int pivot = myArray[pivotIndex];
int i = 0 , j = max-1;
// Pointer to begining of array and one to the end
int* begin = myArray;
int* end = &myArray[max-1];
// While begin < end
while( begin < end )
{
// Find the lowest bound number to swap
while( *begin < pivot )
{
begin++;
}
while( *end > pivot )
{
// Find the highest bound number to swap
end--;
}
// Do the swap
swap(begin,end);
}
// Partition left
myQSort(0, pivotIndex-1, myArray);
// Partiion right
myQSort(pivotIndex+1,max, myArray);
}
EDIT--
Code for Swap:
void swap(int* num, int* num2)
{
int temp = *num;
*num = *num2;
*num2 = temp;
}
// sort interval [begin, end)
void myQSort(int* begin, int* end)
{
if(end - begin < 2)
return;
int* l = begin;
int* r = end - 1;
// Initially find a random pivot
int* pivot = l + rand() % (r - l + 1);
while(l != r)
{
// Find the lowest bound number to swap
while(*l < *pivot) ++l;
while(*r >= *pivot && l < r) --r;
// Do the swap
if(pivot == l) { pivot = r; }
std::swap(*l, *r);
}
// Here l == r and numbers in the interval [begin, r) are lower and in the interval [l, end) are greater or equal than the pivot
// Move pivot to the position
std::swap(*pivot, *l);
// Sort left
myQSort(begin, l);
// Sort right
myQSort(l + 1, end);
}
You're not using the min parameter in your code, anywhere. You need to set begin and your pivot value using that.
I tried working out the codes above. But, they don't compile.
#Mihran: Your solution is correct algorithmically but the following line generates an error:
myQSort(min, begin - myArray, myArray);
This is because begin is of type int* and myArray is of type long, following which the compiler shows this error message:
implicit conversion loses integer precision
Here's a working solution in C++:
#include <iostream>
using namespace std;
void mySwap(int& num1, int& num2){
int temp = num1;
num1 = num2;
num2 = temp;
}
void myQsort(int myArray[], int min, int max){
int pivot = myArray[(min + max) / 2];
int left = min, right = max;
while (left < right) {
while (myArray[left] < pivot) {
left++;
}
while (myArray[right] > pivot) {
right--;
}
if (left <= right) {
mySwap(myArray[left], myArray[right]);
left++;
right--;
}
}
if (min < right) {
myQsort(myArray, min, right);
}
if (left < max) {
myQsort(myArray, left, max);
}
}
int main()
{
int myArray[] = {1, 12, -5, 260, 7, 14, 3, 7, 2};
int min = 0;
int max = sizeof(myArray) / sizeof(int);
myQsort(myArray, min, max-1);
for (int i = 0; i < max; i++) {
cout<<myArray[i]<<" ";
}
return 0;
}
Here's a clear C++ implementation, for reference:
#include <iostream>
#include <vector>
using namespace std;
int partition(std::vector<int>& arr, int low, int high) {
// set wall index
int wall_index = low;
int curr_index = low;
int pivot_elem = arr[high]; // taking last element as pivot_element
// loop through the entire received arr
for (int i = curr_index; i < high; ++i) {
// if element is less than or equal to pivot_elem
// swap the element with element on the right of the wall
// i.e swap arr[i] with arr[wall_index]
if (arr[i] <= pivot_elem) {
// swap
int temp = arr[wall_index];
arr[wall_index] = arr[i];
arr[i] = temp;
// move the wall one index to the right
wall_index++;
curr_index++;
} else {
// if the element is greater than the pivot_element
// then keep the wall at the same point and do nothing
curr_index++;
}
}
// need to swap the pivot_elem i.e arr[high] with the element right of the wall
int temp = arr[wall_index];
arr[wall_index] = arr[high];
arr[high] = temp;
return wall_index;
}
void quick_sort(std::vector<int>& arr, int low, int high) {
if (low < high) { // element with single arr always have low >= high
int split = partition(arr, low, high);
quick_sort(arr, low, split-1);
quick_sort(arr, split, high);
}
}
int main() {
std::vector<int> data = {6,13,8,4,2,7,16,3,8};
int N = data.size();
quick_sort(data, 0, N-1);
for (int i : data) {
cout << i << " ";
}
return 0;
}
I don't see a clean implementation of Quicksort on SO, so here is my easy to understand implementation
PLEASE DONT USE IN PRODUCTION CODE
This is only for your understanding
// Swap position a with b in an array of integer numbers
void swap(int *numbers, int a, int b){
int temp = numbers[a];
numbers[a] = numbers[b];
numbers[b] = temp;
}
static int partition(int *data, int low, int high) {
int left = low, right = high, pivot = data[low];
while (left < right) {
// Everthing on the left of pivot is lower than the pivot
while ((left <= right) && data[left] <= pivot) // <= is because left is the pivot initially
left++;
// Everything on the right of the pivot is greater than the pivot
while((left <= right) && data[right] > pivot)
right--;
if (left < right)
swap(data, left, right);
}
// Put the pivot in the 'rigthful' place
swap(data, low, right);
return right;
}
// Quicksort
static void quick_sort(int *numbers, int low, int high)
{
if (high > low) {
int p_index = partition(numbers, low, high);
quick_sort(numbers, low , p_index - 1);
quick_sort(numbers, p_index + 1, high);
}
}