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);
}
}
Related
I tried to implement merge sort using C++, however, something went wrong. I have no idea what is wrong.
The following code is what I wrote based on CLRS. I think it is quite easy to understand the meaning.
#include <iostream>
#include <vector>
using namespace std;
void merge(vector<int>& nums, int p, int q, int r);
void mergeSort(vector<int>& nums, int p, int r){
if (p < r) {
int q = (p + r) / 2;
mergeSort(nums, p, q);
mergeSort(nums, q + 1, r);
merge(nums, p, q, r);
}
}
void merge(vector<int>& nums, int p, int q, int r) {
int s1 = p, s2 = q + 1;
vector<int> l1, l2;
for (int i = s1; i <= q; i++) {
l1.push_back(nums[i]);
}
for (int i = s2; i <= r; i++) {
l2.push_back(nums[i]);
}
int left = 0, right = 0;
int idx = 0;
while (left < l1.size() && right < l2.size()) {
if (l1[left] < l2[right]) {
nums[idx] = l1[left++];
}
else {
nums[idx] = l2[right++];
}
idx++;
}
while (left < l1.size()) {
nums[idx++] = l1[left++];
}
while (right < l2.size()) {
nums[idx++] = l2[right++];
}
}
int main() {
vector<int> vect;
vect.push_back(1);
vect.push_back(3);
vect.push_back(12);
vect.push_back(23);
vect.push_back(4);
vect.push_back(11);
vect.push_back(44);
vect.push_back(322);
mergeSort(vect, 0, vect.size() - 1);
for (int i = 0; i < vect.size(); i++) {
cout << vect[i] << endl;
}
return 0;
}
I want to use the program to sort some integers, however, it only shows many duplicate numbers. What's going on? I don't think there is a problem of the merge function.
The code needs a one line fix:
int idx = p; // not idx = 0
Optimized top down using arrays from Wiki article (note bottom up is slightly faster):
void TopDownMerge(int A[], int bgn, int mid, int end, int B[])
{
int i, j, k;
i = bgn, j = mid, k = bgn;
while(1){
if(A[i] <= A[j]){ // if left smaller
B[k++] = A[i++]; // copy left element
if(i < mid) // if not end of left run
continue; // continue
do // else copy rest of right run
B[k++] = A[j++];
while(j < end);
break; // and break
} else { // else right smaller
B[k++] = A[j++]; // copy right element
if(j < end) // if not end of right run
continue; // continue
do // else copy rest of left run
B[k++] = A[i++];
while(i < mid);
break; // and break
}
}
}
void TopDownSplitMerge(int B[], int bgn, int end, int A[])
{
if (end - bgn <= 1) // if run size == 1
return; // consider it sorted
int mid = (end + bgn) / 2;
TopDownSplitMerge(A, bgn, mid, B);
TopDownSplitMerge(A, mid, end, B);
TopDownMerge(B, bgn, mid, end, A);
}
void TopDownMergeSort(int A[], int n) // n = size (not size-1)
{
if(n < 2)
return;
int *B = new int [n]; // 1 time allocate and copy
for(size_t i = 0; i < n; i++)
B[i] = A[i];
TopDownSplitMerge(B, 0, n, A); // sort data from B[] into A[]
delete B;
}
Afterwards, I finally get to fix the bugs of my program. After modification, here is the code:
class Solution {
public:
vector<int> temp;
vector<int> sortArray(vector<int>& nums) {
temp.resize((int)nums.size(), 0);
mergeSort(nums, 0, nums.size() - 1);
return nums;
}
void mergeSort(vector<int>& nums, int start, int end) {
if (start >= end) return;
int middle = (start + end) / 2;
mergeSort(nums, start, middle);
mergeSort(nums, middle + 1, end);
merge(nums, start, middle, end);
}
void merge(vector<int>& nums, int leftStart, int middle, int rightEnd) {
int leftEnd = middle;
int rightStart = middle + 1;
int i = leftStart, j = rightStart;
int index = 0;
while (i <= leftEnd && j <= rightEnd) {
if (nums[i] < nums[j]) {
temp[index] = nums[i++];
}
else {
temp[index] = nums[j++];
}
index++;
}
while (i <= leftEnd) {
temp[index++] = nums[i++];
}
while (j <= rightEnd) {
temp[index++] = nums[j++];
}
for (int i = 0; i < rightEnd - leftStart + 1; i++) {
nums[i + leftStart] = temp[i];
}
}
};
Here is something should be careful next time:
In the merge part, it is difficult to merge in place. It'd be better to use another temp array to store the merged results and update to the target array (nums in this case).
Readable identifers is very recommended (Although the pseudocode of CLRS may not use that part).
Need to use debuggers to find the bug of program {However, it takes like forever to load local variables of VS Code debugers.
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 have a series of growing input values that I want to analyze the runtime of.
int main(){
srand(time(NULL)); //initialize random num generator with time(NULL) as seed
int size_arr[7] = {1000, 10000, 25000, 50000, 100000, 150000, 200000};
// int size_arr[6] = {1000, 10000, 25000, 50000, 100000, 129992};
int size = (sizeof(size_arr)/sizeof(int));
The input array int size_arr[7] works for my Quicksort and insertion sort implementations when passes an array created with the following:
for(int k = 0; k < size; k++){
double sort_arr[size_arr[k]];
for(unsigned int l = 0; l < (sizeof(sort_arr)/sizeof(double)); l++){
sort_arr[l] = random_double(100.00, 1000.00);
}
}
With the double values produced here:
double random_double(double min, double max){
return (max - min) * ((double)rand() / (double)RAND_MAX) + min;
}
However, when I run the array through my merge sort function:
//merge sort algorithm
void merge_sort(double *A, int p, int r){
if(p < r){ //stopping condition
int mid = floor((p + r) / 2); //find array midpoint
merge_sort(A, p, mid); //recursively divide array
merge_sort(A, mid+1, r);
// merge(A, p, mid, r); //merge (sort) sub-arrays
merge_sort_merge(A, p, mid, r);
}
}
void merge_sort_merge(double *A, int left, int mid, int right){
double tmp[right];
int l = left, m = mid+1, sort_index = left;
for(int i = left; i <= right; i++){
tmp[i] = A[i];
}
while(l <= mid && m <= right){
if(tmp[l] <= tmp[m]){
A[sort_index] = tmp[l];
l++;
}else{
A[sort_index] = tmp[m];
m++;
}
sort_index++;
}
while(l <= mid){
A[sort_index] = tmp[l];
sort_index++;
l++;
}
}
It crashes when the input size is exactly, 129992 (i've tested it with the int size_arr[6]).
The full code is as follows:
#include <iostream>
#include <array>
#include <math.h>
#include <stdlib.h>
#include <time.h>
#include <exception>
void merge_sort(double *A, int p, int r);
void merge(double *A, int p, int q, int r);
double random_double(double min, double max);
int main(){
srand(time(NULL)); //initialize random num generator with time(NULL) as seed
int size_arr[7] = {1000, 10000, 25000, 50000, 100000, 150000, 200000};
// int size_arr[6] = {1000, 10000, 25000, 50000, 100000, 129992};
int size = (sizeof(size_arr)/sizeof(int));
std::cout << "Merge Sort:" << std::endl;
for(int k = 0; k < size; k++){
double sort_arr[size_arr[k]];
for(unsigned int l = 0; l < (sizeof(sort_arr)/sizeof(double)); l++){
sort_arr[l] = random_double(100.00, 1000.00);
}
clock_t begin = clock();
try{
merge_sort(sort_arr, 0, (sizeof(sort_arr)/sizeof(double)));
}catch(const std::runtime_error& re){
std::cerr << "Runtime error: " << re.what() << std::endl;
}catch(const std::exception &exc){
std::cerr << exc.what();
}catch(...){
std::cerr << "Fuck" << std::endl;
}
clock_t end = clock();
std::cout << "n = " << size_arr[k] << '\t' << (end - begin) << std::endl;
}
return 0;
}
//merge sort algorithm
void merge_sort(double *A, int p, int r){
if(p < r){ //stopping condition
int mid = floor((p + r) / 2); //find array midpoint
merge_sort(A, p, mid); //recursively divide array
merge_sort(A, mid+1, r);
// merge(A, p, mid, r); //merge (sort) sub-arrays
merge_sort_merge(A, p, mid, r);
}
}
void merge_sort_merge(double *A, int left, int mid, int right){
double tmp[right];
int l = left, m = mid+1, sort_index = left;
for(int i = left; i <= right; i++){
tmp[i] = A[i];
}
while(l <= mid && m <= right){
if(tmp[l] <= tmp[m]){
A[sort_index] = tmp[l];
l++;
}else{
A[sort_index] = tmp[m];
m++;
}
sort_index++;
}
while(l <= mid){
A[sort_index] = tmp[l];
sort_index++;
l++;
}
}
double random_double(double min, double max){
return (max - min) * ((double)rand() / (double)RAND_MAX) + min;
}
I've tried catching exceptions (it hasn't thrown any), using the VS JIT debugger but can't get any useful information out of it, the disassembly is as follows (arrow pointing to the fault):
00000000004100D0 cmp rax,1000h
00000000004100D6 ja 00000000004100BF
00000000004100D8 sub rcx,rax
00000000004100DB or qword ptr [rcx],0 <----
00000000004100DF pop rax
00000000004100E0 pop rcx
It seems impossible to step through > 129k recursions, how do I narrow down where the problem is when I have such large input sizes?
Any help is appreciated
When size_arr[k] == 129'992, the size of
double sort_arr[size_arr[k]];
is 129'992 * sizeof(double) == 129'992 * 8 == 1'039'936. This is just shy of 1'048'576 == 1M (binary Mega).
The C standard does not say where variable length arrays are stored (C++ does not define VLA at all), but typically they are allocated on the stack.
using the VS JIT
Implies that you use windows. The default stack size on windows is 1MB. The 8640 bytes not used by sort_arr are easily exhausted by the rest of your program (mostly by tmp within merge_sort_merge which at the peak will be the size of sort_arr). In other words, you overflow the stack.
Solution: don't use variable length arrays for large arrays. (Don't use VLA at all if you prefer the program to be portable and standard compliant). Instead, use dynamically allocated arrays (std::vector).
#include <iostream>
#include <cstdlib>
using namespace std;
void print(int a[], int sz)
{
for (int i = 0; i < sz; i++) cout << a[i] << " ";
cout << endl;
}
void merge(int a[], const int low, const int mid, const int high)
{
int *temp = new int[high-low+1];
int left = low;
int right = mid+1;
int current = 0;
// Merges the two arrays into temp[]
while(left <= mid && right <= high)
{
if(a[left] <= a[right])
{
temp[current] = a[left];
left++;
}
else { // if right element is smaller that the left
{
// if right element is smaller that the left
temp[current] = a[right];
right++;
}
current++;
}
// Fills the array
// The temp array has already been filled
// Use the right side of array to fill temp
if(left > mid)
{
for(int i=right; i <= high;i++)
{
temp[current] = a[i];
current++;
}
}
// Use the left side of array to fill temp
else
{
for(int i=left; i <= mid; i++)
{
temp[current] = a[i];
current++;
}
}
//Fill into original array
for(int i=0; i<=high-low;i++)
{
a[i+low] = temp[i];
}
delete[] temp;
}
void merge_sort(int a[], const int low, const int high)
{ // <-- Error #68
if(low >= high) return;
int mid = (low+high)/2;
merge_sort(a, low, mid); //left half
merge_sort(a, mid+1, high); //right half
merge(a, low, mid, high); //merge them
}
int main()
{ //<-- Error #77
int a[] = {26, 5, 33, 6, 19, 69, 99};
int arraySize = sizeof(a)/sizeof(int);
print(a, arraySize);
merge_sort(a, 0, (arraySize-1));
print(a, arraySize);
return 0;
} //<-- Error #87
// This code is supposed to implement the Merge sort algorithm in c++.
However when ever i compile my code it runs into a bunch of errors.
mergesort.cpp: In function ‘void merge(int*, int, int, int)’:
mergesort.cpp:68: error: a function-definition is not allowed here before ‘{’ token
mergesort.cpp:77: error: a function-definition is not allowed here before ‘{’ token
mergesort.cpp:87: error: expected ‘}’ at end of input
I have indicated where the erros are in the code
Can anyone help me please?
#include <iostream>
#include <cstdlib>
using namespace std;
void print(int a[], int sz)
{
for (int i = 0; i < sz; i++) cout << a[i] << " ";
cout << endl;
}
void merge(int a[], const int low, const int mid, const int high)
{
int *temp = new int[high-low+1];
int left = low;
int right = mid+1;
int current = 0;
// Merges the two arrays into temp[]
while(left <= mid && right <= high)
{
if(a[left] <= a[right])
{
temp[current] = a[left];
left++;
}
else
{
// if right element is smaller that the left
temp[current] = a[right];
right++;
}
current++;
}
// Fills the array
// The temp array has already been filled
// Use the right side of array to fill temp
if(left > mid)
{
for(int i=right; i <= high;i++)
{
temp[current] = a[i];
current++;
}
}
// Use the left side of array to fill temp
else
{
for(int i=left; i <= mid; i++)
{
temp[current] = a[i];
current++;
}
}
//Fill into original array
for(int i=0; i<=high-low;i++)
{
a[i+low] = temp[i];
}
delete[] temp;
}
void merge_sort(int a[], const int low, const int high)
{
if(low >= high) return;
int mid = (low+high)/2;
merge_sort(a, low, mid); //left half
merge_sort(a, mid+1, high); //right half
merge(a, low, mid, high); //merge them
}
main()
{
int a[] = {26, 5, 33, 6, 19, 69, 99};
int arraySize = sizeof(a)/sizeof(int);
print(a, arraySize);
merge_sort(a, 0, (arraySize-1));
print(a, arraySize);
}
You are getting this error because you write an extra { on line 27, which will mess up all those { } matching.
Delete that you will pass the compile.
Hope this helps.
I'm trying to implement quick sort and figuring out the handling part for worst case of it. When pivot choose largest or lowest of element in the array, I got stuck in the middle of my algorithm and have no idea how to handle it.
#include <iostream>
void swap(int item1, int item2)
{
int temp = item1;
item1 = item2;
item2 = temp;
}
int partition(int array[], unsigned int left, unsigned int right)
{
int pivot = array[left];
int pivot_index = left;
for(++left, right; left <= right;)
{
if(array[left] >= pivot && array[right] < pivot)
swap(array[left], array[right]);
if(array[left] < pivot)
left++;
if(array[right] >= pivot)
right++;
}
swap(array[right], pivot_index);
return right;
}
void quicksort(int array[], unsigned int left, unsigned int right)
{
if(left < right)
{
int index = partition(array, left, right);
quicksort(array, 0, index - 1);
quicksort(array, index + 1, right);
}
}
int main()
{
int unsortedarray[] = {10, 0, 9, 3, 4, 5, 8, 1};
int length = sizeof(unsortedarray) / sizeof(int);
quicksort(unsortedarray, 0, length - 1);
for(unsigned int index = 0; index < static_cast<unsigned int>(length); ++index)
std::cout << unsortedarray[index] << std::endl;
return 0;
}
Your swap is working with local copies of the values to be swapped; it isn't making any changes to the arguments.