Related
I'm a French student and trying to calculate the execution time of the Merge Sort algorithm for different size of array.
I also want to write the different execution time in a .csv file. But when my program tries to sort an array with 1 million elements the process returns -1073741571 (0xC00000FD) in Code::Blocks. So if you could point me to a way to find a solution I would be very grateful!
Here is my code:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
void genTab(int *tab, int n) {
int i;
for (i = 0; i < n; i++) {
tab[i] = rand() % 100;
}
}
void fusion(int *tab, int deb, int mid, int fin) {
int i = deb;
int j = mid + 1;
int k = deb;
int temp[fin + 1];
while ((i <= mid) && (j <= fin)) {
if (tab[i] <= tab[j]) {
temp[k] = tab[i];
i++;
} else {
temp[k] = tab[j];
j++;
}
k++;
}
while (i <= mid) {
temp[k] = tab[i];
i++;
k++;
}
while (j <= fin) {
temp[k] = tab[j];
k++;
j++;
}
for (i = deb; i <= fin; i++) {
tab[i] = temp[i];
}
}
void triFusion(int *tab, int i, int j) {
if (i < j) {
triFusion(tab, i, (int)((i + j) / 2));
triFusion(tab, (int)((i + j) / 2 + 1), j);
fusion(tab, i, (int)((i + j) / 2), j);
}
}
void reset(int *tab1, int *tab2, int n) {
for (int i = 0; i < n; i++) {
tab2[i] = tab1[i];
}
}
int main() {
srand(time(NULL));
clock_t start, end;
int nbrTest[15] = {
1000, 5000, 10000, 50000, 80000, 100000, 120000, 140000,
150000, 180000, 200000, 250000, 300000, 450000, 1000000
};
FILE *fp;
char *tpsExecution = "exeTime.csv";
fp = fopen(tpsExecution, "w");
fprintf(fp, "Array Size; Merge Time");
for (int i = 0; i < 15; i++) {
int n = nbrTest[i];
printf("Calculating time for an array of %d \n", n);
int *tab = malloc(sizeof(int) * n);
genTab(tab, n);
int *copie = malloc(sizeof(int) * n);
reset(tab, copie, n);
start = clock();
triFusion(tab, 0, n - 1);
end = clock();
float tpsFusion = (float)(end - start) / CLOCKS_PER_SEC;
reset(tab, copie, n);
printf("writing in the file\n");
fprintf(fp, "\n%d;%f", n, tpsFusion);
free(tab);
free(copie);
}
fclose(fp);
return 0;
}
int temp[fin+1]; may exceed the space limit for the stack. You should allocate it with malloc instead, and free it with free.
If you want to exclude malloc and free from the timed code, the allocation could be performed outside the timed code and passed in as work space.
(Note: posted after the answer from #Eric Postpischil).
The function
void fusion(int * tab, int deb, int mid, int fin)
Has the line
int temp[fin+1];
and the value of fin comes through another function from the number of elements n to be sorted
triFusion(tab, 0, n-1);
and as an automatic variable, breaks the stack when n is large.
I suggest replacing the line with
int *temp = malloc((fin+1) * sizeof *temp);
if(temp == NULL) {
puts("malloc");
exit(1);
}
// ...
free(temp);
fusion() is always allocating the full size of the array for temp, even when only a small fraction of temp is being used. You could change this to:
int k = 0;
...
int temp[fin+1-deb];
...
tab[i]=temp[i-deb];
still this will exceed stack space if n is large. So as suggested in the other answers:
int k = 0;
...
int *temp = malloc((fin+1-deb)*sizeof(int));
...
tab[i]=temp[i-deb];
...
free(temp)
or better still, do a one time allocation of a second array in main or in a "helper" function, the include a pointer to the second array in the merge sort functions.
I am trying to count the number of swaps that occur in my quicksort in C. However, I am getting values that are incorrect and not sure where I went wrong. I am using a structures as my arrays to be sorted.
struct anArray{
int numbers[maxSize];
int swaps;
};
/* Partition function */
int partition(struct anArray *array, int start, int end){
if(start == end){
return start;
}
int pivot = array->numbers[end];
int low = start - 1;
int high = end;
for(;;){
do{
low++;
} while(array->numbers[low] < pivot);
do{
high--;
} while(array->numbers[high] > pivot);
/* Detector for when the cells meet */
if(low >= high){
swap(array, low, end);
return low;
}
}
/* Swapping the values */
swap(array, low, high);
}
This is my partition function used to "separate" the arrays.
void quickSort(struct anArray *array, int start, int end){
if(end - start <= 0){ return; }
else{
int pivot = array->numbers[end];
int partitionPoint = partition(array, start, end);
quickSort(array, start, partitionPoint - 1);
quickSort(array, partitionPoint + 1, end);
}
}
This is my quicksorting function. It's a recursive function.
My swap function increments counter by 1 every time it's called.
In my main, I set
myArray->swaps = counter;
But the number of times the swaps occurs isn't right. For example, if I sort an array that goes from 1 to 9, the number of swaps should be 0 but I get 9. I've tried incrementing counter when it's in the partition function only but it gives me the same result.
Is there something wrong with my partition function?
Thank you very much
Edit 1:
Here's my swap function.
void swap(struct anArray *array, int first, int second){
int temp = array->numbers[first];
array->numbers[first] = array->numbers[second];
array->numbers[second] = temp;
counter++;
}
I've tried using
void swap(struct anArray *array, int first, int second, int swapCount)
and then have swapCount be array->swaps when calling the swap function, and incrementing it by 1 but it gives me the same answer.
Here's a part of my main.
int main(){
struct anArray *ascending = (struct anArray*)malloc(10 * sizeof(struct anArray));
int ascend[maxSize] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
initArray(ascending, ascend);
quickSort(ascending, 0, maxSize - 1);
ascending->swaps = counter;
printf("Test: Unique random values\nSorted: [ ");
for(int i = 0; i < maxSize; i++){
printf("%i ", ascending->numbers[i]);
}
printf("]\nSwaps: %i\nComps: \n\n", ascending->swaps);
The other parts of my main are just other arrays to be sorted. The initArray is used to set the values of array->numbers and also reset array->swaps to 0.
Your quicksort code seems pretty good. I didn't examine it rigorously, but it passed a simple test, so I didn't investigate further. (Edit: Based on your feedback, I created a third version in my second update that shows that the sort has an issue for larger data inputs).
The main bug was the malloc at the top of main. We do not want an array of the struct anArray:
struct anArray *ascending = malloc(10 * sizeof(struct anArray));
That is, we do not want (e.g.) 10 structs, we want a single struct and to fill in 10 ints that go into the numbers field that is in that single struct.
The initArray function was not posted, so I had to guess/deduce what it might be. Based on the above bug, I'm not sure that numbers would have been initialized correctly.
From the code fragments posted, I was able to piece together a whole program. I've created two versions:
One with the bugs annotated [but not fixed] that compiles cleanly.
And, a second that is fully cleaned up, working, and generalized for arbitrary array sizes [please pardon the gratuitous style cleanup]
Here is [something close to] your original code with the bugs annotated:
#include <stdio.h>
#include <stdlib.h>
// NOTE/BUG: this was not defined and _fixed_ defines should be all caps
#define maxSize 10
struct anArray {
int numbers[maxSize];
int swaps;
};
int counter;
void
initArray(struct anArray *array,const int *src)
{
for (int idx = 0; idx < maxSize; ++idx)
array->numbers[idx] = src[idx];
array->swaps = 0;
}
void
swap(struct anArray *array, int first, int second)
{
int temp = array->numbers[first];
array->numbers[first] = array->numbers[second];
array->numbers[second] = temp;
counter++;
}
/* Partition function */
int
partition(struct anArray *array, int start, int end)
{
if (start == end) {
return start;
}
int pivot = array->numbers[end];
int low = start - 1;
int high = end;
for (;;) {
do {
low++;
} while (array->numbers[low] < pivot);
do {
high--;
} while (array->numbers[high] > pivot);
/* Detector for when the cells meet */
if (low >= high) {
swap(array, low, end);
return low;
}
}
/* Swapping the values */
swap(array, low, high);
}
void
quickSort(struct anArray *array, int start, int end)
{
if (end - start <= 0) {
return;
}
else {
// NOTE/BUG: pivot is _not_ used
int pivot = array->numbers[end];
int partitionPoint = partition(array, start, end);
quickSort(array, start, partitionPoint - 1);
quickSort(array, partitionPoint + 1, end);
}
}
int
main(void)
{
// NOTE/BUG: we do _not_ want an array of the struct, but an array of int
// that is allocated for "number" _inside_ the struct
struct anArray *ascending = malloc(10 * sizeof(struct anArray));
int ascend[maxSize] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
// NOTE/BUG: this was not defined
initArray(ascending, ascend);
quickSort(ascending, 0, maxSize - 1);
ascending->swaps = counter;
printf("Test: Unique random values\nSorted: [ ");
for (int i = 0; i < maxSize; i++) {
printf("%i ", ascending->numbers[i]);
}
printf("]\nSwaps: %i\nComps: \n\n", ascending->swaps);
return 0;
}
Here is a cleaned up and working version. I've generalized it so it can take an arbitrarily long array. I've also done a bit of style and code cleanup:
#include <stdio.h>
#include <stdlib.h>
typedef struct {
int *numbers;
int size;
int swaps;
} Array;
Array *
initArray(const int *src,int size)
{
Array *array = malloc(sizeof(Array));
array->numbers = malloc(size * sizeof(int));
array->size = size;
// store in reverse order so the sort will actually do something
for (int idx = 0; idx < size; ++idx)
array->numbers[size - 1 - idx] = src[idx];
array->swaps = 0;
return array;
}
void
freeArray(Array *array)
{
free(array->numbers);
free(array);
}
void
swap(Array *array, int first, int second)
{
int temp = array->numbers[first];
array->numbers[first] = array->numbers[second];
array->numbers[second] = temp;
array->swaps += 1;
}
/* Partition function */
int
partition(Array *array, int start, int end)
{
if (start == end)
return start;
int pivot = array->numbers[end];
int low = start - 1;
int high = end;
for (;;) {
do {
low++;
} while (array->numbers[low] < pivot);
do {
high--;
} while (array->numbers[high] > pivot);
/* Detector for when the cells meet */
if (low >= high) {
swap(array, low, end);
return low;
}
}
/* Swapping the values */
swap(array, low, high);
}
void
quickSort(Array *array, int start, int end)
{
if (end - start <= 0)
return;
//int pivot = array->numbers[end];
int partitionPoint = partition(array, start, end);
quickSort(array, start, partitionPoint - 1);
quickSort(array, partitionPoint + 1, end);
}
int
main(void)
{
// NOTE/BUG: we do _not_ want an array of the struct, but an array of int
// that is allocated for "number" _inside_ the struct
int original[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
int size = sizeof(original) / sizeof(original[0]);
Array *ascending = initArray(original, size);
quickSort(ascending, 0, ascending->size - 1);
printf("Test: Unique random values\nSorted: [ ");
for (int i = 0; i < ascending->size; i++) {
int expected = original[i];
int actual = ascending->numbers[i];
printf("%d%s ", actual, (actual == expected) ? "" : "???");
}
printf("]\nSwaps: %i\nComps: \n\n", ascending->swaps);
freeArray(ascending);
return 0;
}
UPDATE:
What does the line int size = sizeof(original) / sizeof(original[0]); do exactly?
Does it give me an integer for size which I set to be the size of how many numbers I can hold in an array?
Yes, that is common/idiomatic trick to get the count of the number of elements of a fixed size array:
int array[] = { 1, 2, 3 };
size_t count = sizeof(array) / sizeof(array[0]);
Here, sizeof(array) is 3 times the size [in bytes] of the individual elements [which are int, which is 4 bytes], so we have 3 * 4 or 12.
sizeof(array[0]) is the size of the single, first element of the array, which is [again] an int, so this is 4.
So, when we divide the two, we have 12 / 4 or 3, which is the number of elements.
If so, wouldn't the amount of numbers I can hold be really small if sizeof(original[0]) happens to be very large?
No, because of the division. It doesn't care how large the element size [in bytes] is, because the ratio always produces the number of elements.
The sizeof(arr) / sizeof(arr[0]) trick is useful to get the count when we do: int arr[] = { ... };
If we do:
#define ARRCOUNT 3
int arr[ARRCOUNT] = { 1, 2, 3 };
We already know the count (i.e. it is ARRCOUNT).
The [slight] advantage to the sizeof/sizeof trick is that if we had incorrectly defined ARRCOUNT as 4 by mistake, it would still compile, link, and run, but would produce incorrect results [because there were only 3 elements].
This is a common enough trick that we can define a generic macro [that we can reuse by putting it a .h file]:
#define ARRAY_COUNT(arr_) (sizeof(arr_) / sizeof(arr_))
UPDATE #2:
I've tried your code (even tried copying and pasting it) but my swaps is still showing 9 despite my array to be sorted is just going from { 1 to 10}. Not sure why this keeps occurring.
I believe [now] you have a bug in the sort itself.
I've produced another version that has much more extensive test data generation and comparison.
At a minimum, because of the way the tests are structured, the first element of the sorted array should always have a value of 1.
The test that fails is the one that does a random shuffle of the original array before sending it in to be sorted.
You can add other tests as needed. The array needn't be so large to show the problem. For example, the following single test is enough to produce the error:
bigtest(100,237,1);
Anyway, here is the enhanced diagnostic code:
#include <stdio.h>
#include <stdlib.h>
#define MAXLEN 60
typedef struct {
int *numbers;
int size;
int swaps;
} Array;
Array *
initArray(const int *src,int size,int randshuf)
{
int idx;
Array *array = malloc(sizeof(Array));
array->numbers = malloc(size * sizeof(int));
array->size = size;
array->swaps = 0;
// store in reverse order so the sort will actually do something
switch (randshuf) {
case 0: // reverse the numbers
for (idx = 0; idx < size; ++idx)
array->numbers[size - 1 - idx] = src[idx];
break;
default: // do _crude_ random shuffle
for (idx = 0; idx < size; ++idx)
array->numbers[idx] = 0;
for (idx = 0; idx < size; ++idx) {
while (1) {
int ridx = rand() % size;
if (array->numbers[ridx] == 0) {
array->numbers[ridx] = src[idx];
break;
}
}
}
break;
}
return array;
}
void
freeArray(Array *array)
{
free(array->numbers);
free(array);
}
void
swap(Array *array, int first, int second)
{
int temp = array->numbers[first];
array->numbers[first] = array->numbers[second];
array->numbers[second] = temp;
array->swaps += 1;
}
/* Partition function */
int
partition(Array *array, int start, int end)
{
if (start == end)
return start;
int pivot = array->numbers[end];
int low = start - 1;
int high = end;
for (;;) {
do {
low++;
} while (array->numbers[low] < pivot);
do {
high--;
} while (array->numbers[high] > pivot);
/* Detector for when the cells meet */
if (low >= high) {
swap(array, low, end);
return low;
}
}
/* Swapping the values */
swap(array, low, high);
}
void
quickSort(Array *array, int start, int end)
{
if (end - start <= 0)
return;
//int pivot = array->numbers[end];
int partitionPoint = partition(array, start, end);
quickSort(array, start, partitionPoint - 1);
quickSort(array, partitionPoint + 1, end);
}
void
print_orig(const int *orig,int count)
{
int len = 0;
printf("Test: Original numbers (%d):\n",count);
for (int idx = 0; idx < count; ++idx) {
len += printf(" %10d ", orig[idx]);
if (len >= MAXLEN) {
printf("\n");
len = 0;
}
}
if (len > 0)
printf("\n");
}
int
print_array(Array *array,const int *orig,const char *reason)
{
int len = 0;
int cmp;
int err = -1;
printf("Test: Array Values (%s):\n",reason);
for (int idx = 0; idx < array->size; ++idx) {
int actual = array->numbers[idx];
if (orig != NULL) {
int expected = orig[idx];
cmp = (actual == expected);
}
else
cmp = 1;
len += printf(" %10d%c", actual, cmp ? ' ' : '?');
if (len >= MAXLEN) {
printf("\n");
len = 0;
}
if (cmp)
continue;
if (err < 0)
err = idx;
}
if (orig != NULL)
printf("\nSwaps: %i\nComps: \n\n", array->swaps);
else {
if (len > 0)
printf("\n");
}
return err;
}
void
bigtest(int count,int randgap,int randshuf)
// count -- number of elements (negative means random)
// randgap -- gap between element values (negative means random)
// randshuf -- 0=simple reverse, 1=random shuffle
{
int *orig;
Array *array;
printf("\n");
for (int idx = 1; idx <= 80; ++idx)
printf("-");
printf("\n");
printf("COUNT: %d, RANDGAP: %d, RANDSHUF: %d\n",count,randgap,randshuf);
// get number of elements
if (count < 0)
count = (rand() % count) + 1;
// get element gap (e.g. 1 --> {1, 2, 3}, 2 --> { 1, 3, 5 }
if (randgap < 0)
randgap = (rand() % randgap) + 1;
printf("COUNT: %d, RANDGAP: %d, RANDSHUF: %d\n",count,randgap,randshuf);
// get original array
orig = malloc(sizeof(int) * count);
// fill in original array
do {
int val = 1;
// simple gap
if (randgap >= 0) {
if (randgap == 0)
randgap = 1;
for (int idx = 0; idx < count; ++idx, val += randgap)
orig[idx] = val;
break;
}
// random gap
int gap;
for (int idx = 0; idx < count; ++idx, val += gap) {
orig[idx] = val;
gap = (rand() % randgap) + 1;
}
} while (0);
print_orig(orig,count);
array = initArray(orig,count,randshuf);
print_array(array,NULL,"Shuffled");
quickSort(array, 0, array->size - 1);
print_array(array,orig,"Sorted");
freeArray(array);
free(orig);
}
int
main(void)
{
bigtest(10,0,0);
bigtest(-100,23,0);
bigtest(-1000,-2337,0);
bigtest(-1000,-2337,1);
return 0;
}
I'm working through an algorithms MOOC and have a small program that takes an array A of ints in arbitrary order, counts the number of inversions (an inversion being the number of pairs (i,j) of array indices with i<j and A[i] > A[j]).
Below is the code I've written. I'm trying to tackle it using a "divide and conquer" approach where we recursively split the input array into two halves, sort each half individually while counting the inversions and then merge the two halves.
The trick is I need to keep track of the number of inversions and sort the arrays, so I pass the original array around the various recursive calls as an argument to the function and pass the count of inversions as a return value.
The code executes correctly through the first set of recursive calls that successively divide and sort [1,5,3], however when I get to the 3rd invocation of mergeAndCountSplitInv it crashes at the line:
sortedArrayLeft = realloc(sortedArrayLeft, sizeof(int)*(rightLen + leftLen));
with the error:
malloc: *** error for object 0x100103abc: pointer being realloc'd was not allocated
I can't see where I'm not using malloc correctly and I've combed through this checking to see I'm doing the pointer arithmetic correctly and can't spot any errors, but clearly error(s) exist.
Any help is appreciated.
// main.c
// inversionInC
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
// function to help with debugging array/pointer arithmetic
void logArrayLenAndContents (char *arrayName, int arrayToPrint[], int arrayLen){
printf("%s\n", arrayName);
printf("len:%d\n", arrayLen);
for (int idx = 0; idx < arrayLen; idx++) {
printf("array[%d]: %d\n", idx, arrayToPrint[idx]);
}
}
int mergeAndCountSplitInv(int sortedArrayLeft[], int leftLen, int sortedArrayRight[], int rightLen)
{
printf("Calling mergeAndCount with sortedArrayLeft:\n");
logArrayLenAndContents("left Array", sortedArrayLeft, leftLen);
printf("...and sortedArrayRight:\n");
logArrayLenAndContents("right Array", sortedArrayRight, rightLen);
int i = 0;
int j = 0;
int k = 0;
int v = 0; // num of split inversions
int* outArray;
outArray = malloc((leftLen + rightLen) * sizeof(int));
while (i < leftLen && j < rightLen) {
if (sortedArrayLeft[i] < sortedArrayRight[j]) {
outArray[k] = sortedArrayLeft[i];
i++;
} else{
outArray[k] = sortedArrayRight[j];
v += leftLen - i;
j++;
}
k++;
}
// if at the end of either array then append the remaining elements
if (i < leftLen) {
while (i < leftLen) {
outArray[k] = sortedArrayLeft[i];
i++;
k++;
}
}
if (j < rightLen) {
while (j < rightLen) {
outArray[k] = sortedArrayRight[j];
j++;
k++;
}
}
printf("Wrapping up mergeAndCount where outArray contains:\n");
logArrayLenAndContents("outArray", outArray, k);
sortedArrayLeft = realloc(sortedArrayLeft, sizeof(int)*(rightLen + leftLen));
return v;
}
int sortAndCount(int inArray[], int inLen){
printf("Calling sortAndCount with:\n");
logArrayLenAndContents("inArray", inArray, inLen);
if (inLen < 2) {
return 0;
}
int inArrayLenPart1 = ceil(inLen/2.0);
int inArrayLenPart2 = inLen - inArrayLenPart1;
int* rightArray = malloc(sizeof(int) * inArrayLenPart2);
rightArray = &inArray[inArrayLenPart1];
int x = sortAndCount(inArray, inArrayLenPart1);
printf("sortAndCount returned x = %d\n\n", x);
int y = sortAndCount(rightArray, inArrayLenPart2);
printf("sortAndCount returned y = %d\n\n", y);
int z = mergeAndCountSplitInv(inArray, inArrayLenPart1, rightArray, inArrayLenPart2);
printf("mergeAndCount returned z = %d\n", z);
return x+y+z;
}
int main(int argc, const char * argv[])
{
static int* testArray;
testArray = malloc(5 * sizeof(int));
for (int i = 0; i<=4; i++) {
testArray[0] = 1;
testArray[1] = 5;
testArray[2] = 3;
testArray[3] = 2;
testArray[4] = 4;
}
int x = sortAndCount(testArray, 5);
printf("x = %d\n", x);
return 0;
}
This happens because the value of sortedArrayLeft gets lost as soon as the function returns. The realocated value does not make it to the caller, so inArray of the sortAndCount may be pointing to freed memory if realloc needs to reallocate and copy.
In order to fix this, pass a pointer to the pointer, letting sortedArrayLeft to propagate back to inArray of sortAndCount:
int mergeAndCountSplitInv(int **sortedArrayLeft, int leftLen, int sortedArrayRight[], int rightLen) {
...
*sortedArrayLeft = realloc(*sortedArrayLeft, sizeof(int)*(rightLen + leftLen));
return v;
}
...
int sortAndCount(int **inArray, int inLen) {
...
int z = mergeAndCountSplitInv(inArray, inArrayLenPart1, rightArray, inArrayLenPart2);
}
...
int x = sortAndCount(&testArray, 5);
I have this working merge-sort algorithm in C. But it works only for integers. When I tried to change int to char, i'm getting segfault.
Can you please help me, what should I change in this code, so I could use MergeSort like this:
char*str = "test_string";
MergeSort(str, 0, strlen(str)-1);
void Merge(int *array, int left, int mid, int right){
int tempArray[right-left+1];
int pos=0,lpos = left,rpos = mid + 1;
while(lpos <= mid && rpos <= right){
if(array[lpos] <= array[rpos]){
tempArray[pos++] = array[lpos++];
}
else{
tempArray[pos++] = array[rpos++];
}
}
while(lpos <= mid) tempArray[pos++] = array[lpos++];
while(rpos <= right)tempArray[pos++] = array[rpos++];
int iter;
for(iter = 0;iter < pos; iter++){
array[iter+left] = tempArray[iter];
}
return;
}
void MergeSort(int *array, int left, int right){
int mid = (left+right)/2;
if(left<right){
MergeSort(array,left,mid);
MergeSort(array,mid+1,right);
Merge(array,left,mid,right);
}
return;
}
I'm lost. Thanks!
Change your declaration of array from int * to char * in both functions. Make tempArray a char[] instead of an int[]. You are trying to read memory that is 4x (or 8x) out of bounds at the end of the array, hence the seg-fault. Put another way, char is 1 byte (usually) while int is 4 or 8, so you are looking at items of a different size stacked next to each other. Also, do not pass in a const * for your string. Declaring a string as char*str = "test_string"; implies read-only memory on some systems. Use char str[] = "test_string"; instead. If you are not using strictly C, you can use C++ templates to make a function that works for int and char: http://www.codeproject.com/Articles/257589/An-Idiots-Guide-to-Cplusplus-Templates-Part-1
#include <stdio.h>
#include<ctype.h>
#include<string.h>
int Run_count=-1;
int main ( int argc , char *argv[] )
{
/* if you dont want to use argv, put the elements in A yourself,
size being the number of string*/
/*L --> left side, R --> right side*/
int i = 0;
int size = argc-1;
char *A[argc-1];
for(i=1;i<=argc;i++){*(A+i-1) = argv[i];}
Caller(A,size);
for(i=0;i<size;i++){
printf("%s\n", A[i]);
}
printf("%d",Run_count);
}
int Caller(char* A[] , int n){
Run_count++;
int sizeL, sizeR ,i;
char *L[n/2+1] , *R[n-n/2+1];
if (n < 2){return 1;}
sizeL = n/2;
sizeR = n - sizeL;
for(i=0;i<sizeL;i++) {L[i] = *(A+i);}
for(i=0;i<n - n/2;i++) {R[i] = *(A+i+n/2);}
Caller( L, sizeL);
Caller( R, sizeR);
merger( L,sizeL, R,sizeR, A);
}
void merger(char* L[], int lengthL , char* R[] , int lengthR , char *A[]){
int i, j, k ,t =0 ;
for(k = 0 , j = 0; k < lengthL && j < lengthR ;t++){
if(compare(*(L+k),*(R+j))){
*(A+t) = *(L+k);
k++;}
else{*(A+t) = *(R+j);j++;}
}
while(k < lengthL ){
*(A+t) = *(L+k);
k++;t++;
}
while(j < lengthR ){
*(A+t) = *(R+j);
j++;t++;}
}
int compare(char *line1 , char *line2 )
{
int i;
for(i = 0;*(line1 + i) != '\0' && *(line2 + i) != '\0' ;){
if(isdigit(*(line1+i)) && isalpha(*(line2+i))){return 0;}
else if(isdigit(*(line2+i)) && isalpha(*(line1+i))){return 1;}
else if(*(line1 + i) > *(line2 + i)){return 0;}
else if(*(line1 + i) == *(line2 + i)){i++;}
else{return 1;}
}
}
I'm writing a quicksort algorithm to sort an array of strings.
The problem is that my array with the data seem to be overwritten with something right after i allocate the right and left quicksort arrays, because i print the array and its all there, but after i use malloc to allocate the others arrays, i print it again and i'm missing some elements.
Here's the output:
Pivot: 2
Emma, Olivia, Victoria, Gwyneth, Chloe, Hayley, Scarlett,
Emma, Olivia, Victoria, Gwyneth, , , ,
Anyone knows whats happening? What am missing?
char **concatenate(char **array1, int n1, char *pivot, char **array2, int n2, int len){
int i=0, j=0;
int elements = n1 + n2 + 1;
// alocating array
char **concat = (char**) malloc(sizeof(*concat) * elements);
concat[0] = (char*) malloc(sizeof(*concat) * elements * len);
for(i=1; i<elements; i++)
concat[i] = &(concat[0][i*len]);
// concatenating
for(i=0; i<n1; i++)
concat[i] = array1[i];
concat[i++] = pivot;
for(j=0; j<n2; j++)
concat[i++] = array2[j];
// returning
return concat;
}
char **quicksort(char **array, int elements, int len){
// array is already sorted
if(elements < 2)
return array;
int pivot;
int i=0, l=0, r=0;
// selecting the pivot (median)
if(elements % 2 == 0)
pivot = ((elements + 1) / 2) -1;
else
pivot = (elements / 2) -1;
//REMOVE
printf("Pivot: %d\n", pivot);
for(i=0; i<elements; i++)
printf("%s, ", array[i]);
printf("\n");
// alocating arrays
char **left = (char**) malloc(sizeof(*left) * pivot);
left[0] = (char*) malloc(sizeof(*left) * pivot * len);
for(i=1; i<pivot; i++)
left[i] = &(left[0][i*len]);
char **rigth = (char**) malloc(sizeof(*rigth) * pivot);
rigth[0] = (char*) malloc(sizeof(*rigth) * pivot * len);
for(i=1; i<pivot; i++)
rigth[i] = &(rigth[0][i*len]);
//REMOVE
for(i=0; i<elements; i++)
printf("%s, ", array[i]);
printf("\n");
//quicksorting
for(i=0; i<elements; i++){
if(array[i] == array[pivot])
continue;
int comp = strcmp(array[i], array[pivot]);
//REMOVE
printf("%d: strcmp %s, %s is %d\n", i, array[i], array[pivot], comp);
if(comp < pivot)
left[l++] = array[i];
else
rigth[r++] = array[i];
}
//REMOVE
printf("concatenate(");
for(i=0; i<l; i++)
printf("%s ", left[i]);
printf("|%s| ", array[pivot]);
for(i=0; i<r; i++)
printf("%s ", rigth[i]);
printf(")\n");
// recursion and return
return concatenate(quicksort(left, l, len), l, array[pivot], quicksort(rigth, r, len), r, len);
}
int main(int argc, char *argv[]){
int i, j, aux;
char **teste = (char**) malloc(sizeof(*teste) * 7);
teste[0] = (char*) malloc(sizeof(*teste) * 7 * 128);
for(i=1; i<7; i++)
teste[i] = &(teste[0][i*128]);
teste[0] = "Emma";
teste[1] = "Olivia";
teste[2] = "Victoria";
teste[3] = "Gwyneth";
teste[4] = "Chloe";
teste[5] = "Hayley";
teste[6] = "Scarlett";
quicksort(teste, 7, 128);
printf("AFTER\n");
for(i=0; i<7; i++)
printf("%s, ", teste[i]);
printf("\n");
return 0;
}
There is zero reason to allocate for quicksort, and in fact the function can easily suffice in your case with a simple interface of quicksort(char *arr[], unsigned int len), using pointer-math for the subsequence invocations.
Provide a swap algorithm for exchanging pointers:
void swap_str_ptrs(char const **arg1, char const **arg2)
{
const char *tmp = *arg1;
*arg1 = *arg2;
*arg2 = tmp;
}
Then the algorithm is:
void quicksort_strs(char const *args[], unsigned int len)
{
unsigned int i, pvt=0;
if (len <= 1)
return;
// swap a randomly selected value to the last node
swap_str_ptrs(args+((unsigned int)rand() % len), args+len-1);
// reset the pivot index to zero, then scan
for (i=0;i<len-1;++i)
{
if (strcmp(args[i], args[len-1]) < 0)
swap_str_ptrs(args+i, args+pvt++);
}
// move the pivot value into its place
swap_str_ptrs(args+pvt, args+len-1);
// and invoke on the subsequences. does NOT include the pivot-slot
quicksort_strs(args, pvt++);
quicksort_strs(args+pvt, len - pvt);
}
Thats everything. including the partitioning.
How It Works
There are two general recursive quicksort algorithms: the squeeze, and the sweep. This is the sweep algorithm. We march up the sequence, swapping any element "less" than than pivot value (which is swapped to the end of the sequence before the loop starts) to a target slot, the index of which is initially the beginning of the sequence and increases with each swap operation. When the "sweep" is finished, the pvt index is where the pivot value belongs, as everything below that slot is "less" than the that value. So one more swap is made to put the pivot value into position. After that we have two partitions, which are recursed. It is vital that the slot we just identified as the pivot location is not included in either of those partitions. It is the only value we know is in its final resting place.
Test Harnass
Including the above code, we test this with a basic set of strings purposely out of order:
void print_list(char const *args[], unsigned len)
{
unsigned i=0;
for (;i<len;++i)
puts(args[i]);
}
int main()
{
char const *args[] =
{
"this", "is", "a", "test", "of", "quicksort", "with", "strings"
};
srand((unsigned)time(NULL));
quicksort_strs(args, sizeof(args)/sizeof(*args));
print_list(args, sizeof(args)/sizeof(*args));
return 0;
}
Output
a
is
of
quicksort
strings
test
this
with
Non-recursive implementation
It should be noted that the above algorithm lends itself beautifully to a non-recursive implementation. A local dynamic stack is used for holding pairs of data: an pointer and a length. Optimized to not push trivial segments (segments of length 1 or 0) on to the stack, one implementation would like like this:
void quicksort_strs(char const *args[], unsigned int len)
{
// holds our non-recursive stack of segments
struct segment
{
char const **arr;
unsigned int len;
struct segment* next;
} *stack = NULL;
stack = malloc(sizeof(*stack));
stack->arr = args;
stack->len = len;
stack->next = NULL;
while (stack != NULL)
{
unsigned int i, pvt=0;
struct segment *tmp = stack;
stack = stack->next;
// pull values and delete segment record
args = tmp->arr;
len = tmp->len;
free(tmp);
// nothing to unary segments
if (len <= 1)
continue;
// swap a randomly selected value to the last node
swap_str_ptrs(args+((unsigned int)rand() % len), args+len-1);
// reset the pivot index to zero, then scan
for (i=0;i<len-1;++i)
{
if (strcmp(args[i], args[len-1]) < 0)
swap_str_ptrs(args+i, args+pvt++);
}
// move the pivot value into its place
swap_str_ptrs(args+pvt, args+len-1);
// lhs segment push
if (pvt > 1)
{
tmp = malloc(sizeof(*tmp));
tmp->arr = args;
tmp->len = pvt;
tmp->next = stack;
stack = tmp;
}
// rhs segment push
if ((len - ++pvt) > 1)
{
tmp = malloc(sizeof(*tmp));
tmp->arr = args+pvt;
tmp->len = len-pvt;
tmp->next = stack;
stack = tmp;
}
}
}
Obviously having a canned node-stack implementation would shorten this up considerably, but the idea should be readily apparent. A realloc() schema for holding nodes on the end of the "stack" rather than the beginning would be equally interesting, as it would eliminate the need to next pointer management, replaced with a top index instead.
Anyway, good luck, and I hope it helps.