MergeSort and Merge C++ - c++

Having an issue with my MergeSort() and Merge() in my homework for school. Im new to C++ (I've been out of school for two years, they changed my major with new classes. I was learning java....) Long story short, my Merge() and MergeSort() functions aren't working in VS Code when I debug them. I'm having issues with the function working in general. I don't know if I'm passing the parameters correctly.
I've tried to change up the Merge() function by moving it up above MergeSort().
void Merge(int B[], int &lStart, int &lEnd, int &rStart, int &rEnd) {
int length;
int temp[length];
int theSaved = lStart;
int i = lStart;
while (lStart < lEnd && rStart <= rEnd) {
if (B[lStart] < B[rStart]) {
temp[i++] = B[lStart++];
}
else {
temp[i++] = B[rStart++];
}
}
while (lStart <= lEnd) {
temp[i++] = B[lStart++];
}
while (rStart <= rEnd) {
temp[i++] = B[rStart++];
}
for (int j = theSaved; j < rEnd; j++) {
B[j] = temp[j];
}
}
void MergeSort(int B[], int start, int end) {
int middle;
int theMiddle = middle + 1;
if (start < end) {
middle = (start + end) / 2;
MergeSort(B, start, middle);
MergeSort(B, middle + 1, end);
Merge(B, start, middle, theMiddle, end);
}
}
int main() {
int length = 7;
int begin = 0;
int end = (length - 1);
int B[length] = {10, 50, 90, 60, 5, 20, 40};
for (int i = 0; i < length; i++) {
MergeSort(B, begin, end);
cout << "\n" << B[i];
}
return 0;
}
I just wanted some insight into what's going on. I tried to do it logically but it ended up being too confusing. The output should be 5,10,20,40,50,60,90

merge() should not be taking parameters by reference, use int lStart instead of int &lStart. rStart will always be lEnd + 1, so only 3 parameters are needed for merge. The if in merge should be lStart <= lEnd.
Although not a problem, the code would be a bit simpler if the initial call from main is MergeSort(B, 0, length). Then Merge could be called with (B, start, middle, end). The loop in merge() would have ... lStart < middle ... rStart < end ... for the loops and if statements.

Related

What is wrong with my merge sort implementation refer to CLRS?

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.

Sort an Array Given an array of integers nums, sort the array in ascending order. Input: nums = [5,2,3,1] Output: [1,2,3,5] Inp nums = [5,1,1,2,0,0]

AddressSanitizer:DEADLYSIGNAL
==32==ERROR: AddressSanitizer: stack-overflow on address 0x7ffdd2294ff8 (pc 0x000000345b86 bp 0x7ffdd22950d0 sp 0x7ffdd2295000 T0)
==32==ABORTING
#include <vector>
class Solution {
public:
void helper(std::vector<int>& nums, int start, int end) {
if (start <= end) {
int mid = (start + end) / 2;
helper(nums, start, mid);
helper(nums, mid + 1, end);
int i = start;
int j = mid;
int k = mid + 1;
int l = end;
int m = 0;
int ans[nums.size()];
while (i < mid && k < end) {
if (nums[i] < nums[j]) {
ans[m++] = nums[i++];
}
else {
ans[m++] = nums[j++];
}
}
while (i < mid) {
ans[m++] = nums[i++];
}
while (j < end) {
ans[m++] = nums[j++];
}
i = start;
j = end;
while (i <= j) {
nums[i++] = ans[i++];
}
}
}
std::vector<int> sortArray(std::vector<int>& nums) {
int n = nums.size() - 1;
helper(nums, 0, n);
std::vector<int>finalans;
for (int i = 0; i < nums.size(); i++) {
finalans.push_back(nums[i]);
}
return finalans;
}
};
You have many problems in your code.
1st, it does not compile. It contains invalid C++ code. VLAs (Variabale length arrays) are not a part of the C++ language. So, your line int ans[nums.size()]; will not compile with a C++ compliant compiler.
2nd, and this is the root course of your crash, you do not have end end conditions for your recursion. In the 3rd line of your helper function, you call helper recursively and then again and again and again and again . . .
Until you get a stackoverflow or out of memory and the above error.
What you need to do is: Look in your algorithm book and try to understand mergesort. Then read it again and again and again. If you finally understood the algorithm (My guess is that it needs 10 days), then start coding. Not before. Otherwise you will definitely and always fail.
Try it.

Stack Overflow while sorting (merge sort and quick sort)

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.

c++ recursive quicksort infinite loop

So I've been having a problem with a c++ program that quicksorts an array of integers. When I have more than six elements in my array, the sort infinitely loops for some reason. I think I've isolated the problem to the choosing of mm pivotal value, but I can't work out for the life of me why it's causing it to break.
#include<iostream>
using namespace std;
int getPivot(int begin,int end){//Takes length of array as input and returns the position of the pivot
int len = end-begin;
if(len < 3){
return end;
}else{
return 2;
}
};
void quickSort(int begin, int end, int arr[]){
int pivot = arr[getPivot(begin,end)];//get Pivotal value - If I replace this with just 0 then there are no problems...
int tempLeft = begin, tempRight = end;
int temp;
while(tempLeft <= tempRight){
while(arr[tempLeft] < pivot){//Find a point where there are 2 elements that need to be swapped
tempLeft++;
}
while(arr[tempRight] > pivot){
tempRight--;
}
if(tempLeft <= tempRight){
temp = arr[tempLeft];//Swap the elements
arr[tempLeft] = arr[tempRight];
arr[tempRight] = temp;
tempLeft++;//Skip these swapped elements in the sort
tempRight--;
}
}
if (begin < tempRight){//Only recurse lower if the new sub array has a length greater than 1
quickSort(begin, tempRight, arr);
}
if (tempLeft < end){
quickSort(tempLeft, end, arr);
}
}
main() {
int array[] = {0,1,2,3,4,5,6};
int length = 7;
quickSort(0,length-1,array);
}
You will probably ask why I have such a weird way of choosing my pivotal value, but lets just say that for this instance the pivotal value has to be the third element in each sublist or if the sub list is smaller than 3 it is the last item in the sublist.
The reason I think the problem is associated with the pivotal value is because when I replace my method of choosing a pivot with just using the first element in the sublist I don't have any problems.
If run, as is now the program, will segfault after looping infinitely but if the array being sorted is one element shorter, it will work fine. That has had me baffled for hours now, and I can't work out what the problem is. If anyone has any tips or suggestions, they would be greatly appreciated.
quickSort(3,6,arr) will always call quickSort(3,6,arr).
I think you miss
int getPivot(int begin,int end)
{
//Takes length of array as input and returns the position of the pivot
int len = end-begin;
if(len < 3){
return end;
}else{
return 2+begin; // Here
}
};
EDIT: Clarification
GetPivot(3,6) will return 2, instead should return an index between 3 and 6.
You could also use the median of three approach for your pivot. It is a little more robust.
#include<iostream>
using namespace std;
void myswap(int* arr, int left, int right)
{
int swap = arr[left];
arr[left] = arr[right];
arr[right] = swap;
}
int getPivotIndex(int* arr, int begin, int end){
int middle = (begin + end) / 2;
if (arr[end] < arr[begin])
{
myswap(arr, begin, end);
}
if (arr[middle] < arr[begin])
{
myswap(arr, middle, begin);
}
if (arr[end] < arr[middle])
{
myswap(arr, end, middle);
}
return middle;
};
void quickSort(int begin, int end, int* arr){
int pivot = arr[getPivotIndex(arr, begin, end)];
int tempLeft = begin, tempRight = end;
while (tempLeft <= tempRight){
while (arr[tempLeft] < pivot){
tempLeft++;
}
while (arr[tempRight] > pivot){
tempRight--;
}
if (tempLeft <= tempRight){
myswap(arr, tempLeft, tempRight);
tempLeft++;
tempRight--;
}
}
if (begin < tempRight){
quickSort(begin, tempRight, arr);
}
if (tempLeft < end){
quickSort(tempLeft, end, arr);
}
}
int main() {
int array[] = { 6, 0, 2, 5, 4, 3, 1 }; // Test we are actually sorting
int length = 7;
quickSort(0, length - 1, array);
for (int i = 0; i < length; i++)
{
std::cout << "array[" << i << "]: " << array[i] << endl;
}
return 0;
}

Why is my merge sort not working?

It compiles fine, but when it runs, it adds random high numbers to the list, as well as duplicates of existing numbers. I've had a couple of people look over this, and none of them can figure it out.
void mergeSort(int list[], int length) {
recMergeSort(list, 0, length - 1);
}
void recMergeSort(int list[], int first, int last) {
if (first < last) {
int mid = (first + last) / 2;
recMergeSort(list, first, mid);
recMergeSort(list, mid + 1, last);
merge(list, first, last, mid);
}
}
void merge(int list[], int first, int last, int mid) {
int arraySize = last - first + 1;
int* tempList = new int[arraySize];
int beginPart1 = first;
int endPart1 = mid;
int beginPart2 = mid + 1;
int endPart2 = last;
int index = beginPart1;
while (beginPart1 <= endPart1 && beginPart2 <= endPart2) {
if (list[beginPart1] < list[beginPart2]) {
tempList[index] = list[beginPart1];
beginPart1++;
}
else {
tempList[index] = list[beginPart2];
beginPart2++;
}
index++;
}
while (beginPart1 <= endPart1) {
tempList[index] = list[beginPart1];
index++;
beginPart1++;
}
while (beginPart2 <= endPart2) {
tempList[index] = list[beginPart2];
index++;
beginPart2++;
}
for (int i = first; i <= last; i++) {
list[i] = tempList[i - first];
}
delete[] tempList;
}
In function merge(), you're incorrectly calculating the index variable:
Assuming begin = 10, mid = 14, end = 19 (for a total array size of 0 .. 19, and you're recMergeSort()ing the higher half), your index = 10, but the tempList array is indexed 0..9 (because arraySize = last - first + 1 == 10).
So you're overflowing your tempList array, and when you "merge", you get data corruption.
Fix your index variable to be 0-based (rather than beginPart1 based).
I think problem is here:
int index = beginPart1;
It should be
int index = 0;
If I run this in C# I get an IndexOutOfRangeException on the following line:
tempList[index] = list[beginPart1];
I reckon if you trace it through you are probably running off the end of a buffer somewhere hence the random numbers.