This is a merge- sort algorithm I wrote. Although, it works well for smaller arrays, it gives a segmentation fault for arrays containing more than 7/8 elements. It also fails in some cases where a number is repeated. For example - {5,5,1,2,1}. I have been trying to identify the error but to no avail
I know that the code is not completely efficient but I am concentrating on making it work right now. Suggestions regarding the improvements in the code will be helpful. Thank you in advance.
#include <iostream>
using namespace std;
void printarray(int a[], int size);
void splitsort(int b[], int start, int end); //Split array into half
void merge(int b[], int start, int end); // merge the sorted arrays
int main()
{
cout << "This is merge sort" << endl;
int array[] = { 9,8,7,6,5,4,3,2,1 };
int length = sizeof(array) / sizeof(array[0]);
printarray(array, length);
splitsort(array, 0, length - 1);
cout << "sorted array" << endl;
printarray(array, length);
return 0;
}
void printarray(int a[], int size) {
for(int i = 0; i<size; i++) {
cout << a[i] << ",";
}
cout << endl;
return;
}
void splitsort(int b[], int start, int end) {
//base case
if(end == start) { return; }
//
splitsort(b, start, (start + end) / 2);
splitsort(b, (start + end) / 2 + 1, end);
merge(b, start, end);
return;
}
void merge(int b[], int start, int end) {
int tempb[(end - start) + 1];
//base case
if(end == start) { return; } // if single element being merged
int i = start;
int j = (start + end) / 2 + 1;
for(int k = start; k <= end; k++) {
if(i == (start + end) / 2 + 1) { tempb[k] = b[j]; j++; }// finished first array
else if(j == end + 1) { tempb[k] = b[i]; i++; }// finished second array
else if(b[i] >= b[j]) {
tempb[k] = b[j];
j++;
}
else if(b[j] >= b[i]) {
tempb[k] = b[i];
i++;
}
}
for(int k = start; k <= end; k++) {
b[k] = tempb[k];
}
return;
}
int tempb[(end - start) + 1];
tempb can have as few as 2 elements, while the main array has 10 elements. You end up accessing tempb[9], causing segmentation fault.
To fix the problem, change the size to int tempb[max_size]; where max_size is the size of array as calculated earlier int length = sizeof(array) / sizeof(array[0]);
Changing tempb to std::vector<int> tempb(max_size) will help in debugging as well as being compliant with C++ standard.
Related
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 made a quicksort algorithm as normal functions within main, then attempted to transfer it to a class (per what my instructor wanted). However, I am now getting a segmentation fault error, but I cannot figure out where it is occurring. Source code below; thanks.
main.cpp
#include "MergeSort.h"
#include "QuickSort.h"
#include <iostream>
using namespace std;
const int SIZE = 10;
int main() {
cout << "This is compiling.\n";
int testArray[SIZE] = {5,3,9,2,1,3,8,1,7,9};
for (int i = 0; i < SIZE; i++) {
cout << testArray[i];
}
QuickSort test(testArray, SIZE);
int * result = test.getArray();
cout << endl;
for (int i = 0; i < SIZE; i++) {
cout << result[i];
}
return 0;
}
QuickSort.cpp
#include "QuickSort.h"
//constructor
QuickSort::QuickSort(const int anArray[], int aSize) {
array_p = new int[aSize];
size = aSize;
for (int i = 0; i < size; i++)
array_p[i] = anArray[i];
quickSort(0, aSize - 1);
return;
}
//destructor
QuickSort::~QuickSort() {
delete [] array_p;
return;
}
//accessor function for array
int * QuickSort::getArray() {
return array_p;
}
//PRIV MEM FUNCTIONS
void QuickSort::quickSort(int start, int end)
{
if (start == end)
return;
int pivot;
pivot = partition(array_p, start, end);
//quickSort everything before where pivot is now
quickSort(start, pivot - 1);
//quickSort everything after where pivot is now
quickSort(pivot, end);
return;
}
int QuickSort::partition(int a[], int start, int end)
{
int first, last, pivot;
pivot = end;
first = start;
last = end - 1; //minus one is because pivot is at last
while (first < last) {
if (a[first] > a[pivot] && a[last] < a[pivot]) {
swap(a, first, last);
first++;
last--;
}
else {
if (a[first] <= a[pivot])
first++;
if (a[last] >= a[pivot])
last--;
}
}
if (a[pivot] > a[first]) {
swap(a, pivot, first + 1);
return first + 1;
}
else {
swap(a, pivot, first);
return first;
}
}
void QuickSort::swap(int a[], int indexOne, int indexTwo)
{
int temp = a[indexOne];
a[indexOne] = a[indexTwo];
a[indexTwo] = temp;
return;
}
I think it might be
quickSort(start, pivot - 1);
because once I comment it out, I do not get the error; however, I cannot figure out why.
Change quickSort(pivot, end);
to
quickSort(pivot+1, end);
and
if (start == end)
to if (start >= end).
Your code will run just fine now.
I'm trying to create a program merge-sort on an array of int butI keep having troubles running this merge sort, it gives me a segment fault but I couldn't find anything wrong with it. In void mergesort when I put first <= last then the segment fault appears if not, then 5 5 5 5 is being print.
#include <iostream>
using namespace std;
void merge(int *arr, int size, int first, int middle, int last)
{
int temp[size];
for(int i = first; i<=last; i++)
{
temp[i] = arr[i];
}
int i=first, j=middle+1, k=0;
while(i<=middle && j<=last)
{
if(temp[i] <= temp[j])
{
arr[k] = temp[i];
i++;
}
else
{
arr[k]=temp[i];
j++;
}
k++;
}
while(i<=middle)
{
arr[k]=temp[i];
k++;
i++;
}
}
void mergesort(int *arr, int size, int first, int last)
{
if(first<last)
{
int middle = ( first + last )/2;
mergesort(arr,size,first,middle);
mergesort(arr,size,middle+1,last);
merge(arr,size,first,middle,last);
}
}
int main()
{
cout <<"Him";
const int size = 10;
int numbers [] = {5,10,1,6,2,9,3,8,7,4};
mergesort(numbers,size,0,9);
for( int i= 0; i<size; ++i)
{
cout << numbers[i] << " ";
}
return 0;
}
There are (at least) two bugs. This:
else
{
arr[k]=temp[i];
j++;
}
should be this:
else
{
arr[k]=temp[j];
j++;
}
and this:
int i=first, j=middle+1, k=0;
should be this:
int i=first, j=middle+1, k=first;
In general, you ought to learn to step through the code, at least by putting diagnostic output statements here and there. Once you have the hang of that you can move up to a good debugger.
The standard library already implements a function that merges correctly: std::inplace_merge. Implementation adapted from this more general post
void mergesort(int * first, int * last)
{
std::ptrdiff_t N = std::distance(first, last);
if (N <= 1) return;
int * middle = std::next(first, N / 2);
mergesort(first, middle);
mergesort(middle, last);
std::inplace_merge(first, middle, last);
}
int main()
{
cout <<"Him";
const int size = 10;
int numbers [] = {5,10,1,6,2,9,3,8,7,4};
mergesort(numbers, numbers+size);
for( int i= 0; i<size; ++i)
{
cout << numbers[i] << " ";
}
return 0;
}
Suggestion 1:
Instead of that line:
int temp[size];
If you need a dynamic size array use:
int temp = new int[size];
Then once you are done with it
delete[] temp;
Edit: As Neil suggested using std::vector is may be more useful than arrays in such situations (if you are allowed to use it).
Your code has 3 bugs, Also you can reduce your code length too if required.
void merge(int *arr, int size, int first, int middle, int last)
{
int temp[size];
for(int i = first; i<=last; i++)
temp[i] = arr[i];
int i=first, j=middle+1, k=first; // 1st Change, Set k to first instead of 0
while(i<=middle && j<=last)
{
if(temp[i] <= temp[j])
arr[k++] = temp[i++];
else
arr[k++]=temp[j++]; // 2nd Change, use j instead of i
}
while(i<=middle)
arr[k++]=temp[i++];
while(j<=last) // 3rd Change you missed this case
arr[k++]=temp[j++];
}
Live Code
Working on a class project in which i need to implement a Merge Sort to sort 500,000 items.
After many attempts I tried looking for source code online and found some here: http://www.sanfoundry.com/cpp-program-implement-merge-sort/
I had to alter the code to use a dynamic array (for size). When the program runs the merge function, I create a new, dynamic array using the number of elements (or high) that are being merged. Once the function is finished sorting them and merge them into the original array, i use delete[] on the new dynamic array. This is where I get my "Heap Corruption Detected" error.
Here is the code (wall of text):
//Heap Sort
#include <iostream>
#include <fstream>
#include <sstream>
#include <ctime>
#include <stdlib.h>
#include <stdio.h>
using namespace std;
//Function Prototypes
void mergesort(int *a, int low, int high);
void merge(int *a, int low, int high, int mid);
int main()
{
//Start with element 1 of the array
int line_no = 0;
int num;
int array_size = 500000;
int* num_array = new int[array_size];
//Open file for input
fstream in_file("CSCI3380_final_project_dataset.txt", ios::in);
//Test for file opening
if (!in_file)
{
cout << "Cannot open words1.txt for reading" << endl;
exit(-1);
}
//Read file
while(true)
{
//Read one line at a time
in_file >> num;
//Test for eof
if (in_file.eof())
break;
num_array[line_no] = num;
//Increment array position
line_no++;
}
//Close the file
in_file.close();
//Start Time
clock_t time_a = clock();
//Run Sorting Algorithim
mergesort(num_array, 0, array_size-1);
//End Time
clock_t time_b = clock();
//Elapsed Time
if (time_a == ((clock_t)-1) || time_b == ((clock_t)-1))
{
cout << "Unable to calculate elapsed time" << endl;
}
else
{
int total_time_ticks = time_b - time_a;
cout << "Elapsed time: " << total_time_ticks << endl;
}
delete[] num_array;
return 0;
}
void mergesort(int *a, int low, int high)
{
int mid;
if (low < high)
{
mid=(low+high)/2;
mergesort(a,low,mid);
mergesort(a,mid+1,high);
merge(a,low,high,mid);
}
return;
}
void merge(int *a, int low, int high, int mid)
{
//--------------------------Create new array-------------------------------
int* sort_array = new int[high];
//--------------------------New Array Created-----------------------------
int i, j, k;
i = low;
k = low;
j = mid + 1;
while (i <= mid && j <= high)
{
if (a[i] < a[j])
{
sort_array[k] = a[i];
k++;
i++;
}
else
{
sort_array[k] = a[j];
k++;
j++;
}
}
while (i <= mid)
{
sort_array[k] = a[i];
k++;
i++;
}
while (j <= high)
{
sort_array[k] = a[j];
k++;
j++;
}
for (i = low; i < k; i++)
{
a[i] = sort_array[i];
}
//---------------------------Delete the New Array--------------------
delete[] sort_array;
//--------------------------Oh No! Heap Corruption!------------------
}
I'll spare you the "you should be using vectors", "you should be using smart pointers", etc. You should be, and I'll leave it at that. Regarding your actual problem....
You're writing one-past the allocated space of your array. The allocated size is high:
int* sort_array = new int[high];
meaning you can only dereference from 0..(high-1). Yet this:
while (j <= high)
{
sort_array[k] = a[j];
k++;
j++;
}
is one location that is guaranteed to write to sort_array[high], and therefore invoke undefined behavior.
A Different Approach
Mergesort is about div-2 partitioning. You know this. What you may not have considered is that C and C++ both perform pointer-arithmetic beautifully and as such you only need two parameters for mergesort(): a base address and a length. the rest can be taken care of for you with pointer math:
Consider this:
void mergesort(int *a, int len)
{
if (len < 2)
return;
int mid = len/2;
mergesort(a, mid);
mergesort(a + mid, len-mid);
merge(a, mid, len);
}
And a merge implementation that looks like this:
void merge(int *a, int mid, int len)
{
int *sort_array = new int[ len ];
int i=0, j=mid, k=0;
while (i < mid && j < len)
{
if (a[i] < a[j])
sort_array[k++] = a[i++];
else
sort_array[k++] = a[j++];
}
while (i < mid)
sort_array[k++] = a[i++];
while (j < len)
sort_array[k++] = a[j++];
for (i=0;i<len;++i)
a[i] = sort_array[i];
delete[] sort_array;
}
Invoked from main() like the following. Note: I've removed the file i/o in place of a random generation just to make it easier to test:
#include <iostream>
#include <ctime>
#include <cstdlib>
#include <cstdio>
using namespace std;
//Function Prototypes
void mergesort(int *a, int len);
void merge(int *a, int mid, int len);
int main()
{
std::srand((unsigned int)std::time(nullptr));
// Start with element 1 of the array
int array_size = 500000;
int* num_array = new int[array_size];
std::generate_n(num_array, array_size, std::rand);
// Start Time
clock_t time_a = clock();
// Run Sorting Algorithim
mergesort(num_array, array_size);
// End Time
clock_t time_b = clock();
//Elapsed Time
if (time_a == ((clock_t)-1) || time_b == ((clock_t)-1))
{
cout << "Unable to calculate elapsed time" << endl;
}
else
{
int total_time_ticks = time_b - time_a;
cout << "Elapsed time: " << total_time_ticks << endl;
}
delete[] num_array;
return 0;
}
This resulted is an elapsed time of:
Elapsed time: 247287
More Efficient
By now you've seen that you will need at most N-space in addition to you sequence. The top-most merge should e evidence enough of that. What you may not consider is that in-reality that is exactly the space you need, and you can allocate it up-front and use it throughout the algorithm if you desire. You can keep the current entrapping for mergesort(), but we'll be wrapping it up with a front-loader that allocates all the space we'll ever need once:
// merges the two sequences a[0...mid-1] and a[mid...len-1]
// using tmp[] as the temporary storage space
static void merge_s(int *a, int *tmp, int mid, int len)
{
int i=0, j=mid, k=0;
while (i < mid && j < len)
{
if (a[i] < a[j])
tmp[k++] = a[i++];
else
tmp[k++] = a[j++];
}
while (i < mid)
tmp[k++] = a[i++];
while (j < len)
tmp[k++] = a[j++];
for (i=0;i<len;++i)
a[i] = tmp[i];
}
static void mergesort_s(int *a, int *tmp, int len)
{
if (len < 2)
return;
int mid = len/2;
mergesort_s(a, tmp, mid);
mergesort_s(a + mid, tmp+mid, len-mid);
merge_s(a, tmp, mid, len);
}
void mergesort(int *a, int len)
{
if (len < 2)
return;
int *tmp = new int[len];
mergesort_s(a,tmp,len);
delete [] tmp;
}
This resulted in an elapsed time of:
Elapsed time: 164704
Considerably better than we had before. Best of luck.
The copy step shown in WhozCraig's code example can be avoided using a pair of functions to control the direction of the merge (note - a bottom up merge would still be faster).
Note - I wouldn't recommend using either WhozCraig's or my code example, since these methods were probably not covered in your class, and it's supposed to be code written based on what you were taught in your class. I don't know if bottom up merge sort was covered in your class, so I didn't post an example of it.
mergesort_s(int *a, int *tmp, int len)
{
// ...
mergesort_atoa(a, tmp, 0, len);
// ...
}
mergesort_atoa(int *a, int *tmp, int low, int end)
{
if((end - low) < 2){
return;
}
int mid = (low + end) / 2;
mergesort_atot(a, tmp, low, mid);
mergesort_atot(a, tmp, mid, end);
merge_s(tmp, a, low, mid, end);
}
mergesort_atot(int *a, int *tmp, int low, int end)
{
if((end - low) < 2){
tmp[0] = a[0];
return;
}
int mid = (low + end) / 2;
mergesort_atoa(a, tmp, low, mid);
mergesort_atoa(a, tmp, mid, end);
merge_s(a, tmp, low, mid, end);
}
void merge_s(int *src, int *dst, int low, int mid, int end)
{
int i = low; // src[] left index
int j = mid; // src[] right index
int k = low; // dst[] index
while(1){ // merge data
if(src[i] <= src[j]){ // if src[i] <= src[j]
dst[k++] = src[i++]; // copy src[i]
if(i < mid) // if not end of left run
continue; // continue (back to while)
while(j < end) // else copy rest of right run
dst[k++] = src[j++];
return; // and return
} else { // else src[i] > src[j]
dst[k++] = src[j++]; // copy src[j]
if(j < end) // if not end of right run
continue; // continue (back to while)
while(i < mid) // else copy rest of left run
dst[k++] = src[i++];
return; // and return
}
}
}
I am trying to implement merge sort routine in C++ using vectors. But I am getting random numbers after sorting. It doesn't giving any kind of warning either. Please help.
Here is my complete code. I am trying to implement it in a class.
#include <iostream>
#include <vector>
#include <limits>
using namespace std;
class array{
private:
vector<int> num;
public:
array();
void print();
void remove(int element);
void insert(int element);
void sort();
void mergesort(int start, int end);
void merge(int start, int end);
};
array :: array(){
cout << "Enter elements to insert in array(any character to end): ";
int element;
while(cin >> element)
num.push_back(element);
// clearing the input(cin) buffer
cin.clear();
cin.ignore(std::numeric_limits<streamsize>::max(), '\n');
}
void array :: print(){
for(unsigned int i = 0; i < num.size(); i++)
cout << "Element " << i+1 << ": " << num[i] << endl;
}
void array :: remove(int element){
for(unsigned int i = 0; i< num.size(); i++)
if(num[i] == element){
num.erase(num.begin()+i,num.begin()+i+1);
break;
}
}
void array :: insert(int element){
num.push_back(element);
}
void array :: sort(){
if(num.size() <= 1)
return;
else
mergesort(0, num.size()-1);
}
void array :: mergesort(int start, int end){
if(start >= end)
return;
else{
int mid = (start + end)/2;
mergesort(start, mid);
mergesort(mid+1, end);
merge(start, end);
}
}
void array :: merge(int start, int end){
vector<int> num2;
int mid = (start + end)/2;
int i = start;
int j = mid + 1;
cout << "start: " << start << " mid: " << mid << " end: " << end << endl;
while(i <= mid && j <= end)
num2.push_back(num[i] >= num[j] ? num[j++]: num[i++]);
if(i > mid)
while(j <= end)
num2.push_back(num[j++]);
else
while(i <= mid)
num2.push_back(num[i++]);
for(unsigned int i = 0; i <= num.size(); i++)
num[i] = num2[i];
}
The most obvious issue is the last loop in merge()
for(unsigned int i = 0; i <= num.size(); i++)
num[i] = num2[i];
Indexes start and end are passed to the function but this code blindly tries to copy num.size()+1 elements from your temporary vector to the beginning of num. I think something like this is more correct:
for(unsigned int i = 0; i < num2.size(); i++)
num[start + i] = num2[i];
What "warning" were you expecting from a syntactically correct C++ program? As long as the syntax is correct, you won't get any "warning".
So what debugging have you done? That is the first thing you should do.