I'm attempting to teach myself the basics of algorithms and data structures through a free online course, and as such, I though it'd give it a first shot at merge sort. This isn't really going to be used for anything so it's pretty sloppy, but I seem to be having a problem where main is not calling the MergeSort function.
The output is 00000000, (I assume because array is never assigned anything). When I run the program through gdb the program seems to get to that line, and then completely skip over the function and go directly to the loop that prints the array.
Any thoughts? Am I missing something stupid?
#include <iostream>
using namespace std;
int *MergeSort(int array[], int sizeOf);
int main(){
int numbers[8] = {5, 4, 1, 8, 7, 2, 6, 3};
int *array = MergeSort(numbers, 8);
for (int i = 0; i < 8; i++)
cout << array[i];
return 0;
}
int *MergeSort(int array[], int sizeOf){
int *leftArr = new int[sizeOf/2]; // Build arrays to split in half
int *rightArr = new int[sizeOf/2];
if (sizeOf < 2){ // Base case to end recursion
return array;
}
else{
for (int i = 0; i < (sizeOf/2); i++){ // Left gets first half
leftArr[i] = array[i];
}
int j = (sizeOf/2) - 1; // Set point to start building 2nd
for (int i = sizeOf; i >= (sizeOf/2); i--){
rightArr[j] = array[i]; // Build other half of array
j--;
}
leftArr = MergeSort(leftArr, sizeOf/2); // Call Recursive functions
rightArr = MergeSort(rightArr, sizeOf/2);
}
static int *newArray = new int[sizeOf]; // Sorted array to Build
int k = 0; // Iterators to build sorted func
int m = 0;
int p = 0;
while (p < sizeOf){
if (leftArr[k] < rightArr[m]){ // Left Arr's current value is less
newArray[p] = leftArr[k]; // right arr's current calue
k++;
}
else if (leftArr[k] >= rightArr[m]){
newArray[p] = rightArr[k];
m++;
}
p++;
}
//for (int i = 0; i < 8; i++)
// cout << newArray[i] << endl;
return newArray; // Return address to new array
}
There is a fundamental design issue in your MergeSort():
your algorithm is recursive (that's perfect)
unfortunately it returns newArraywhich is static. This means that all invocations use the same instance of the same static variable (and overwrite the one returned by the recursive call).
You need to solve this by making newArray non static. And at the end of the function, you need to delete[] the arrays returned by recursive calls in order to avoid memory leakage.
Related
i'm new to c++ and working through the problem of rearranging a sorted array in O(n) time so that first comes the maximum element, then the minimum, then the second max, then the second min, so it goes.
my solution doesn't pass the tests without an auxiliary array result to which I then copy over my values - see below for the initial and the working solutions:
// initial:
void maxMin(int arr[], int size) {
bool switchPointer = true;
int min_ptr = 0;
int max_ptr = size - 1;
for (int i = 0; i < size; i++) {
if (switchPointer) {
arr[i] = arr[max_ptr];
max_ptr--;
} else {
arr[i] = arr[min_ptr];
min_ptr++;
}
switchPointer = !switchPointer;
}
}
// working
void maxMin(int arr[], int size) {
int* result = new int[size];
bool switchPointer = true;
int min_ptr = 0;
int max_ptr = size - 1;
for (int i = 0; i < size; i++) {
if (switchPointer) {
result[i] = arr[max_ptr];
max_ptr--;
} else {
result[i] = arr[min_ptr];
min_ptr++;
}
switchPointer = !switchPointer;
}
for (int j = 0; j < size; j++) {
arr[j] = result[j]; // copying to original array
}
delete [] result;
}
why do we need an auxiliary result array? thank you!
Because if you apply your algorithm "in-place" you will overwrite MIN values of your original array before you could use them. Imagine:
arr = {1, 2, 3, 4, 5}
expected result is {5, 1, 4, 2, 3}
in first iteration you will do arr[0] = arr[4] // arr[0] is equal to 5 now
in second iteration you will do arr[1] = arr[0] // but this is not what you want, because arr[0] was already changed and is not equal to "1" anymore
Usually you use temp variables when you need to read your original source of data and not the modified version. In your case I think the problem arises when you do
arr[i] = arr[max_ptr]; or arr[i] = arr[min_ptr]; in your non working example. In this case you modify the array and you read (arr[max_ptr]) the same overwritten array leading to inconsistencies in your algorithm. Using an auxiliary variable solves the issue since you read the original data but you store it somewhere else.
I am trying to create a merge function for two array structures in c++ but am coming up with a bad access error that I don't know how to solve. The error comes up when I am trying to swap the element in the smaller array into the larger, merged array. The code doesn't even go through a single iteration. All three of i, j, and k remain at 0. Any help would be greatly appreciated! Here is the code:
struct Array
{
int *A;
int size;
int length;
};
void display(Array arr){
for (int i = 0; i < arr.length; i++)
std::cout << arr.A[i] << std::endl;
}
Array merge(Array arr1, Array arr2){
Array arr3;
arr3.length = arr1.length + arr2.length;
arr3.size = arr1.length + arr2.length;
int i = 0, j =0, k =0;
while(i <arr1.length && j < arr2.length){
if (arr1.A[i] < arr2.A[j])
{
arr3.A[k] = arr1.A[i]; //(The error is displayed here: Thread 1: EXC_BAD_ACCESS (code=1, address=0x28))
k++;
i++;
}
else if (arr2.A[j] < arr1.A[i])
{
arr3.A[k] = arr2.A[j];
k++;
j++;
}
}
for (; i< arr1.length; i++)
{
arr3.A[k]=arr1.A[i];
k++;
}
for (; i< arr2.length; j++)
{
arr3.A[k]=arr2.A[j];
k++;
}
return arr3;
}
int main() {
Array arr1;
arr1.size = 10;
arr1.length = 5;
arr1.A = new int[arr1.size];
arr1.A[0]= 2;
arr1.A[1]= 6;
arr1.A[2]= 10;
arr1.A[3]= 15;
arr1.A[4]= 25;
Array arr2;
arr2.size = 10;
arr2.length = 5;
arr2.A = new int[arr2.size];
arr2.A[0]= 3;
arr2.A[1]= 4;
arr2.A[2]= 7;
arr2.A[3]= 18;
arr2.A[4]= 20;
Array arr3 = merge(arr1, arr2);
display(arr3);
return 0;
}
Your Array arr3 does not allocate any memory for its int *A field. It's natural that it would not work.
Anyway, your implementation of Array is very poor. Don't reimplement arrays unless you have a good reason; use std::vector instead.
If you really need to implement an Array on your own, then learn about encapsulation, make a class with a constructor, and allocate/delete your data (*A) field properly. Remember, using pointers and heap memory without understanding them is a recipe for disaster.
Easy: arr3.A is not initialized. It's a pointer. What does it point to?
Suggestion: learn about dynamic memory allocation.
In one of the tutorial videos for merge sort, it was mentioned that once the right and left sub arrays have to merged to the parent array, in order to reduce the space complexity we need to free the memory allocated for the left and right sub arrays. But whenever we come out of the function call, the local variable will be destroyed. Do correct me if I am wrong. So will the action of freeing the memory make any difference?
Here is the code that I wrote:
#include <iostream>
#include <bits/stdc++.h>
using namespace std;
void mergeArr(int *rarr, int *larr, int *arr, int rsize, int lsize) {
int i = 0, r = 0, l = 0;
while (r < rsize && l < lsize) {
if (rarr[r] < larr[l]) {
arr[i++] = rarr[r++];
} else {
arr[i++] = larr[l++];
}
}
while (r < rsize) {
arr[i++] = rarr[r++];
}
while (l < lsize) {
arr[i++] = larr[l++];
}
}
void mergeSort(int *arr, int length) {
if (length > 1) {
int l1 = length / 2;
int l2 = length - l1;
int rarr[l1], larr[l2];
for (int i = 0; i < l1; i++) {
rarr[i] = arr[i];
}
for (int i = l1; i < length; i++) {
larr[i - l1] = arr[i];
}
mergeSort(rarr, l1);
mergeSort(larr, l2);
mergeArr(rarr, larr, arr, l1, l2);
// will free(rarr); free(larr); make any difference in space complexity
}
}
int main() {
int arr[5] = { 1, 10, 2, 7, 5 };
mergeSort(arr, 5);
for (int i = 0; i < 5; i++)
cout << arr[i] << " ";
}
I have multiple things to say about this. More from a C++ pov:
int rarr[l1],larr[l2]; - this is illegal c++. This is just an extension provided by g++ and is not valid across other compilers. You should either do int* rarr = new int[l1]; or even better use an std::vector: std::vector<int> rarr(l1).
If you are doing the former (dynamic allocation using new i.e int* rarr = new int[l1]), you have to manage the memory on your own. So when you're done using it you have to delete it: delete[] rarr. Mind it, malloc and free are not c++, they are c. new and delete are c++ way of allocating/deallocating memory.
If you use a vector, c++ will handle the deletion/deallocation of memory so you need not worry.
Now coming back to your original question: whether or not an idea like this would improve your space complexity: the answer is NO. It won't.
Why? Think about the max temporary storage you're using. Check the first case of your recursion. Isn't the space that you're using O(N)? because larr and rarr will both be of size N/2. Moreover, the space complexity is O(N) assuming the temporary storage is being freed. If somehow the space is not freed, the space complexity will increase to O(N)+2*(N/2)+4*O(N/4).... which is O(Nlog2N) because each step of recursion is allocating some space which it is not freeeing.
In your implementation, the left and right arrays are defined with automatic storage, so deallocation is automatic when the function returns but it poses 2 problems:
a sufficiently large array will invoke undefined behavior because allocating too much space with automatic storage will cause a stack overflow.
variable sized arrays are not standard C++. You are relying on a compiler specific extension.
The maximum stack space used by your function is proportional to N, so the space complexity is O(N) as expected. You could allocate these arrays with new, and of course you would then have to deallocate them with delete otherwise you would have memory leaks and the amount of memory lost would be proportional to N*log2(N).
An alternative approach would use a temporary array, allocated at the initial call and passed to the recursive function.
Note also that the names for the left and right arrays are very confusing. rarr is actually to the left of larr!
Here is a modified version:
#include <iostream>
using namespace std;
void mergeArr(int *larr, int *rarr, int *arr, int lsize, int rsize) {
int i = 0, r = 0, l = 0;
while (l < lsize && r < rsize) {
if (larr[l] <= rarr[r]) {
arr[i++] = larr[l++];
} else {
arr[i++] = rarr[r++];
}
}
while (l < lsize) {
arr[i++] = larr[l++];
}
while (r < rsize) {
arr[i++] = rarr[r++];
}
}
void mergeSort(int *arr, int length) {
if (length > 1) {
int l1 = length / 2;
int l2 = length - l1;
int *larr = new int[l1];
int *rarr = new int[l2];
for (int i = 0; i < l1; i++) {
larr[i] = arr[i];
}
for (int i = l1; i < length; i++) {
rarr[i - l1] = arr[i];
}
mergeSort(larr, l1);
mergeSort(rarr, l2);
mergeArr(larr, rarr, arr, l1, l2);
delete[] larr;
delete[] rarr;
}
}
int main() {
int arr[] = { 1, 10, 2, 7, 5 };
int length = sizeof arr / sizeof *arr;
mergeSort(arr, length);
for (int i = 0; i < length; i++) {
cout << arr[i] << " ";
}
return 0;
}
Freeing temporary arrays does not influence on space complexity because we must consider maximum memory consumption - it is about size of initial array.
From the performance point of view, it seems reasonable to allocate temporary storage once in the beginning of sorting, reuse it at every stage, and free it after all the work is done.
I've tried the (somewhat questionable) convention of deleteing after usage, but that doesn't seem to work. The program is supposed to receive an input of a single integer, sort a randomly created array, and print elapsed time for sorting, yet when I leave the delete in there, the program abnormally ends without even a warning after I do the input. In other words, it crashes. However, when I comment out just the delete line, the program executes perfectly.
The MWE is measuring time for a simple Quick Sort Algorithm, and since this is a school project I cannot change the main() function and using the QuickSort class and its pointers, etc..
The only things I can change are the stuff that goes in the various functions, and although it does seem that set(double*, int) can just be integrated into the constructor, that's not a possible option here for some reason.
The objective is to define a default double* in the constructor, and then delete it and copy input_array into this->arr in set:
EDIT: I use Windows 10 and the GCC C++ Compiler from MinGw-w64. All compilations have been executed in the Windows Command Prompt.
main.cpp
#include <iostream>
#include <cstdlib> // Just for good measure, though this shouldn't be needed
#include "Sort.hpp"
bool check_quick(QuickSort *quick_sort) {
int i = 0;
while(i < (quick_sort->size) - 1) {
if (quick_sort->arr[i] > quick_sort->arr[i + 1]) break;
++i;
} if (i == (quick_sort->size) - 1) return true;
else return false;
}
int main() {
int n; cin >> n;
double *input_array = new double[n];
srand((unsigned int)time(NULL));
for (int k = 0; k < n; k++) input_array[k] = (double)((rand() % n));
QuickSort* quick_sort = new QuickSort();
quick_sort->set(input_array, n);
quick_sort->run();
if (check_quick(quick_sort)) {
cout << "QuickSort is validated" << endl << endl;
} delete quick_sort;
}
Sort.hpp
#define CLOCKS_PER_SECOND 1000
#include <iostream>
#include <ctime>
#include <iomanip> // Use to call setprecision(4)
using namespace std;
class QuickSort {
friend bool check_quick(QuickSort*); // Give access for private variables
public:
void print_time() const {
cout << "QuickSort : " << fixed << setprecision(4) << seconds
<< " sec" << endl;
// << fixed << setprecision(4) always prints to four numbers after point
}
QuickSort() {
this->arr = new double[10];
for (int i = 0; i < 10; ++i) this->arr[i - 1] = i; // Set default array
seconds = clock(); // Set current Millisecond to starting time
}
~QuickSort() {
delete this->arr; // Delete array in object of this class
}
void sorter(double *arr, int begin, int end) { // Sorting Recursive Function
// Size of array without pivot is: end - begin
int pivot = arr[end];
// PIVOT is element at end of subarray "arr[begin...end]"
int i = begin, j = end;
while (i <= j) {
while (arr[i] < pivot) i++; // Increment until arr[i] is larger than
while (arr[j] > pivot) j--; // Decrement until arr[j] is lesser than
if (i <= j) { // If the larger element precedes lesser element
swap(arr[i], arr[j]); // Call Swap function
i++; j--;
} // If i is larger than j now, i was 1 lesser than j before,
// effectively leaving no more elements to scan.
}
if (begin < j) sorter(this->arr, begin, j); // Recursive, larger part
if (end > i) sorter (this->arr, i, end); // Recursive, lesser part
}
void run() {
sorter(this->arr, 0, this->size - 1); // Call Sorter function
seconds = (double)(clock() - seconds) / (double)(CLOCKS_PER_SECOND);
// Calculate Difference of Ticks and divide by Ticks per second.
// Now, `seconds` is passed seconds with millisecond precision.
}
void set(double *arr, int size) {
this->arr = new double[size]; // Make new array of `size` size
for (int i = 0; i < size; i++) this->arr[i] = arr[i]; // Copy input_arr
for (int i = 0; i < size; i++) cout << this->arr[i] << endl; // Copy input_arr
this->size = size; // Save global `size` to object of class
}
void swap(double &p, double &q) { // Swap Function
// Ampersand precedence to change input
double x = p; // Temporary `double` saver
p = q; // p is q
q = x; // q is x, which is p
}
private:
double *arr;
int size;
double seconds;
};
In your QuickSort constructor you are writing outside the bounds of the array that you are allocating:
this->arr = new double[10];
for (int i = 0; i < 10; ++i) this->arr[i - 1] = i; // Set default array
In the first iteration i is 0 so this->arr[i - 1] writes to the element -1. This is only crashing when you call delete as if you don't the runtime doesn't notice this corruption and exits cleanly.
Presumably just changing i-1 to i will produce the desired behaviour.
The first line of QuickSort::set:
this->arr = new double[size]; // Make new array of `size` size
leaks the array allocated in QuickSort's constructor. You need to delete[] that array first, before assigning arr to point to another one:
delete[] arr;
this->arr = new double[size]; // Make new array of `size` size
If this were the real world, and not a shcool assignment, it would be much better not to use a raw pointer in this case. Rather, a std::vector<double> would be much more appropriate.
A quick look reveals that you are using delete instead of delete[]..
this line:
delete this->arr; // Delete array in object of this class
should be:
delete[] this->arr; // Delete array in object of this class
Additionally, as per the convention of delete you should also do this:
delete[] this->arr;
this->arr = nullptr; // This is important, else your are double deleting which is calling for trouble.
I have this function
void shuffle_array(int* array, const int size){
/* given an array of size size, this is going to randomly
* attribute a number from 0 to size-1 to each of the
* array's elements; the numbers don't repeat */
int i, j, r;
bool in_list;
for(i = 0; i < size; i++){
in_list = 0;
r = mt_lrand() % size; // my RNG function
for(j = 0; j < size; j++)
if(array[j] == r){
in_list = 1;
break;
}
if(!in_list)
array[i] = r;
else
i--;
}
}
When I call this function from
int array[FIXED_SIZE];
shuffle_array(array, FIXED_SIZE);
everything goes all right and I can check the shuffling was according to expected, in a reasonable amount of time -- after all, it's not that big of an array (< 1000 elements).
However, when I call the function from
int *array = new int[dynamic_size];
shuffle_array(array, dynamic_size);
[...]
delete array;
the function loops forever for no apparent reason. I have checked it with debugging tools, and I can't say tell where the failure would be (in part due to my algorithm's reliance on random numbers).
The thing is, it doesn't work... I have tried passing the array as int*& array, I have tried using std::vector<int>&, I have tried to use random_shuffle (but the result for the big project didn't please me).
Why does this behavior happen, and what can I do to solve it?
Your issue is that array is uninitialized in your first example. If you are using Visual Studio debug mode, Each entry in array will be set to all 0xCC (for "created"). This is masking your actual problem (see below).
When you use new int[dynamic_size] the array is initialized to zeros. This then causes your actual bug.
Your actual bug is that you are trying to add a new item only when your array doesn't already contain that item and you are looking through the entire array each time, however if your last element of your array is a valid value already (like 0), your loop will never terminate as it always finds 0 in the array and has already used up all of the other numbers.
To fix this, change your algorithm to only look at the values that you have put in to the array (i.e. up to i).
Change
for(j = 0; j < size; j++)
to
for(j = 0; j < i; j++)
I am going to guess that the problem lies with the way the array is initialized and the line:
r = mt_lrand() % size; // my RNG function
If the dynamically allocated array has been initialized to 0 for some reason, your code will always get stack when filling up the last number of the array.
I can think of the following two ways to overcome that:
You make sure that you initialize array with numbers greater than or equal to size.
int *array = new int[dynamic_size];
for ( int i = 0; i < dynnamic_size; ++i )
array[i] = size;
shuffle_array(array, dynamic_size);
You can allows the random numbers to be between 1 and size instead of between 0 and size-1 in the loop. As a second step, you can subtract 1 from each element of the array.
void shuffle_array(int* array, const int size){
int i, j, r;
bool in_list;
for(i = 0; i < size; i++){
in_list = 0;
// Make r to be betwen 1 and size
r = rand() % size + 1;
for(j = 0; j < size; j++)
if(array[j] == r){
in_list = 1;
break;
}
if(!in_list)
{
array[i] = r;
}
else
i--;
}
// Now decrement the elements of array by 1.
for(i = 0; i < size; i++){
--array[i];
// Debugging output
std::cout << "array[" << i << "] = " << array[i] << std::endl;
}
}
You are mixing C code with C++ memory allocation routines of new and delete. Instead stick to pure C and use malloc/free directly.
int *array = malloc(dynamic_size * sizeof(int));
shuffle_array(array, dynamic_size);
[...]
free(array);
On a side note, if you are allocating an array using the new[] operator in C++, use the equivalent delete[] operator to properly free up the memory. Read more here - http://www.cplusplus.com/reference/new/operator%20new[]/