C: Top-Down Merge Sort - why infinite recursion? - c

I'm trying to implement merge sort, where the original and auxiliary array are alternated for each recursion. It's based on a this Java code. The description reads as follows (Link):
Improvements. We can cut the running time of mergesort substantially with some carefully considered modifications to the implementation.
[...]
Eliminate the copy to the auxiliary array. It is possible to eliminate the time (but not the space) taken to copy to the auxiliary array used for merging. To do so, we use two invocations of the sort method, one that takes its input from the given array and puts the sorted output in the auxiliary array; the other takes its input from the auxiliary array and puts the sorted output in the given array. With this approach, in a bit of mindbending recursive trickery, we can arrange the recursive calls such that the computation switches the roles of the input array and the auxiliary array at each level.
The following C code is my attempt to alternate the roles of the two arrays:
#include <stdlib.h>
#include <string.h>
#include "mergesort.h"
#define THRESHOLD 20
static size_t size_m = 0;
static size_t elements = 0;
static size_t mod = 0;
static char *a = NULL;
static char *b = NULL;
static char *i = NULL;
static char *j = NULL;
static char *k = NULL;
static char *start = NULL;
static char *middle = NULL;
static char *end = NULL;
static char *e = NULL;
static int (*cmp_m)(const void *, const void *) = NULL;
void sort(char *a, char *b, size_t lmod, size_t rmod) {
elements = rmod-lmod+1;
//========== INSERTION SORT ==========
if(elements <= THRESHOLD) {
start = b+size_m*lmod;
end = b+size_m*rmod;
for(i = start; i <= end; i += size_m) {
memcpy(e, i, size_m);
for(j = i-size_m; j >= start && (*cmp_m)((void *)e, (void *)j) < 0; j -= size_m) {
memcpy(j+size_m, j, size_m);
}
memcpy(j+size_m, e, size_m);
}
return;
}
//========== SPLIT OPERATION ==========//
size_t mmod = (rmod-lmod)/2;
sort(b, a, lmod, mmod);
sort(b, a, mmod+1, rmod);
//========== CHECK IF CURRENT SUBARRAY IS ALREADY SORTED ==========//
if((*cmp_m)((void *)(a+size_m*mmod), (void *)(a+size_m*(mmod+1))) <= 0) {
memcpy(b+lmod, a+lmod, size_m*elements);
return;
}
//========== MERGE OPERATION ==========//
start = a+size_m*lmod;
middle = a+size_m*mmod;
end = a+size_m*rmod;
i = start;
j = middle+size_m;
for(k = start; k <= end; k += size_m) {
mod = k-a;
if(i <= middle && (j > end || (*cmp_m)((void *)i, (void *)j) <= 0)) {
memcpy(b+mod, i, size_m);
i += size_m;
} else {
memcpy(b+mod, j, size_m);
j += size_m;
}
}
}
void mergesort(void *array, size_t num, size_t size, int (*cmp)(const void *a, const void *b)) {
size_m = size;
threshold = THRESHOLD;
a = (char *)array;
b = (char *)malloc(num*size_m);
e = (char *)malloc(size_m);
cmp_m = cmp;
memcpy(b, a, size_m*num);
sort(b, a, 0, num-1);
free(b);
free(e);
}
After profiling with valgrind, it seems my code does infinite recursion (the message was "can't grow stack").
Why does my implementation do infinite recursion?

Perhaps, valgrind can't judge the element decreases or not by recursion.
Try the following code.
static void sort(char *a, char *b, size_t n) {
:
:
//========== SPLIT OPERATION ==========//
size_t m = n/2;
sort(b, a, m);
sort(b + m * size_m, a + m * size_m, n - m);

Related

Generic implementation of InsertionSort using void pointers

I'm working for this ADT project and i need to implement the insertion sort algorithm and verify that it works fine in an appropriate test function, that apply the algorithm to an array of double, a string and a struct.
I'm using as a guideline this pseudocode:
procedure InsertionSort(a, n)
for i <- 1, (n-1) do
j <- 1
while (j>0) and (a[j] < a[j-1]) do
Swap(a, j-1, j)
end while
end for
end procedure
I can't understand what the problem is.
The test function gives me error on the first assert () [ in test_sort_algorithm(...) ], therefore telling me that the algorithm is not working properly. But I can't understand where the error is. I've tried to recreate the algorithm for a normal array, without using a void pointer, and everything works. So I guess my problem is that I didn't understand the use of void pointers.
Can anyone please help me understand what's wrong with my Insertion sort algorithm?
Thank you.
This is my attempt:
/**
* \brief Sorts the given array according to the insertion sort algorithm.
*
* \param base Pointer to the start of the input array.
* \param n Number of elements in the input array.
* \param size The size (in bytes) of each element of the array.
* \param cmp Pointer to the comparison function used to sort the array in
* ascending order.
* The comparison function is called with two arguments that point to the
* objects being compared and must return an interger less than, equal to, or
* greater than zero if the first argument is considered to be respectively
* less than, equal to, or greater than the second.
*/
void upo_insertion_sort(void *base, size_t n, size_t size, upo_sort_comparator_t cmp)
{
size_t i, j;
unsigned char *ptr = base;
for (i = 1; i <= n-1; i++)
{
j = i;
while ( (j > 0) && (cmp(ptr+j*size, ptr+(j-1)*size) < 0) )
{
swap(ptr+(j-1)*size, ptr+j*size, size);
j = j - 1;
}
}
}
void swap(void *a, void *b, size_t n)
{
void *tmp = malloc(n);
if (tmp == NULL) { abort(); }
memmove(tmp, a, n);
memmove(a, b, n);
memmove(b, tmp, n);
free(tmp);
}
upo_sort_comparator_t cmp is a pointer to a comparison function. Declaration:
/** \brief Type definition for comparison functions used to compare two elements */
typedef int (*upo_sort_comparator_t)(const void*, const void*);
As I say before this function must be tested, to see if the algorithm work properly.
Code:
#define N 9
struct item_s
{
long id;
char *name;
};
typedef struct item_s item_t;
static double da[] = {3.0,1.3,0.4,7.8,13.2,-1.1,6.0,-3.2,78};
static double expect_da[] = {-3.2,-1.1,0.4,1.3,3.0,6.0,7.8,13.2,78.0};
static const char *sa[] = {"The","quick","brown","fox","jumps","over","the","lazy","dog"};
static const char *expect_sa[] = {"The","brown","dog","fox","jumps","lazy","over","quick","the"};
static item_t ca[] = {{9,"john"},{8,"jane"},{7,"mary"},{6,"anthony"},{5,"stevie"},{4,"bob"},{3,"ann"},{2,"claire"},{1,"alice"}};
static item_t expect_ca[] = {{1,"alice"},{2,"claire"},{3,"ann"},{4,"bob"},{5,"stevie"},{6,"anthony"},{7,"mary"},{8,"jane"},{9,"john"}};
/* Comparators */
static int double_comparator(const void *a, const void *b);
static int string_comparator(const void *a, const void *b);
static int item_comparator(const void *a, const void *b);
/* Test cases */
void test_sort_algorithm(void (*sort)(void*,size_t,size_t,upo_sort_comparator_t));
static void test_insertion_sort();
int double_comparator(const void *a, const void *b)
{
const double *aa = a;
const double *bb = b;
return (*aa > *bb) - (*aa < *bb);
}
int string_comparator(const void *a, const void *b)
{
const char **aa = (const char**) a;
const char **bb = (const char**) b;
return strcmp(*aa, *bb);
}
int item_comparator(const void *a, const void *b)
{
const item_t *aa = a;
const item_t *bb = b;
return (aa->id > bb->id) - (aa->id < bb->id);
}
void test_sort_algorithm(void (*sort)(void*,size_t,size_t,upo_sort_comparator_t))
{
int ok = 1;
size_t i = 0;
double *da_clone = NULL;
char **sa_clone = NULL;
item_t *ca_clone = NULL;
ok = 1;
da_clone = malloc(N*sizeof(double));
assert( da_clone != NULL );
memcpy(da_clone, da, N*sizeof(double));
sort(da_clone, N, sizeof(double), double_comparator);
for (i = 0; i < N; ++i)
{
ok &= !double_comparator(&da_clone[i], &expect_da[i]);
}
free(da_clone);
assert( ok );
ok = 1;
sa_clone = malloc(N*sizeof(char*));
assert( sa_clone != NULL );
memcpy(sa_clone, sa, N*sizeof(char*));
sort(sa_clone, N, sizeof(char*), string_comparator);
for (i = 0; i < N; ++i)
{
ok &= !string_comparator(&sa_clone[i], &expect_sa[i]);
}
free(sa_clone);
assert( ok );
ok = 1;
ca_clone = malloc(N*sizeof(item_t));
assert( ca_clone != NULL );
memcpy(ca_clone, ca, N*sizeof(item_t));
sort(ca_clone, N, sizeof(item_t), item_comparator);
for (i = 0; i < N; ++i)
{
ok &= !item_comparator(&ca_clone[i], &expect_ca[i]);
}
free(ca_clone);
assert( ok );
}
void test_insertion_sort()
{
test_sort_algorithm(upo_insertion_sort);
}
int main()
{
printf("Test case 'insertion sort'... ");
fflush(stdout);
test_insertion_sort();
printf("OK\n");
return 0;
}

How to correctly call this merge sort function?

I am trying to implement this merge sort function to sort an array of structs in c. When I call the function my program exits early, I think this is because my array i am sorting is of type row_t* and needs to be row_t**, I am unsure on how to correctly malloc my data in order to achieve this.
//I have copied relevant bits of my code below
//this is the struct i am trying to sort by the value S
typedef struct
{
double rho, u, v, x, y, flux_u, flux_v, S;
} row_t;
//This is where i allocate the array i want to sort
row_t* linear_row_arr = (row_t*)malloc(sizeof(row_t)*100);
//this is where i try to call the function,
//linear_row_arr is an array of row_t, with 100 elements
merge_sort((void**)linear_row_arr, 99, row_array_s_comp);
//This is the function i am trying to call.
void merge(void** array, int n, int mid, int cmp(const void*, const void*))
{
// (0) need extra space for merging
void** tmp = malloc(n * sizeof(void*));
void** left = array;
void** right = array + mid;
int i = 0;
int j = 0;
int left_size = mid;
int right_size = n - mid;
// (1) perform the merge
for (int k = 0; k < n; k++) {
if (j == right_size)
tmp[k] = left[i++];
else if (i == left_size)
tmp[k] = right[j++];
else if (cmp(left[i], right[j]) < 1)
tmp[k] = left[i++];
else
tmp[k] = right[j++];
}
// (2) copy the merged array
for (int i = 0; i < n; i++) {
array[i] = tmp[i];
}
// (3) clean up
free(tmp);
}
void merge_sort(void** array, int n, int cmp(const void*, const void*))
{
if (n > 1) {
int mid = n / 2;
merge_sort(array, mid, cmp);
merge_sort(array + mid, n - mid, cmp);
merge(array, n, mid, cmp);
}
}
int row_array_s_comp(const void* a, const void* b)
{
row_t* ra = (row_t*)a;
row_t* rb = (row_t*)b;
// with int data we can just subtract to get the right behaviour
return ra->S - rb->S;
}
When I run this the code exits early with no error message.
EDIT:
I tried using #Ian Abbott's solution and it produced a seg fault at my comparison function. Could it be that I used malloc instead of calloc to allocate the memory for my data?
// This is my function call
//100 elements of row_t*
merge_sort(linear_row_arr, 100, sizeof(row_t*), row_array_s_comp);
EDIT 2:
Thank you Ian, I have fixed my errors and now have a handy merge sort function at my disposal. I up voted your answer but it says it won be displayed publicly as i have less than 15 rep. If anyone needs it here is the final comparison function i used was
int row_array_s_comp(const void* a, const void* b)
{
row_t* ra = (row_t*)a;
row_t* rb = (row_t*)b;
// with double data we can just subtract to get the right behaviour
return (ra->S > rb->S) - (ra->S < ra->S);
}
and i called the function with
merge_sort(linear_row_arr, 100, sizeof(row_t), row_array_s_comp);
If anyone finds this useful feel free to upvote #Ians Abotts answer as it is correct but I can't.
Thanks again for your time!
Here is a simple implementation of a top-down merge sort of an array, using parameters similar to qsort. Time complexity is O(n log n). It uses temporary storage of similar size to the input array.
/* Subroutine to merge two input arrays into an output array. */
static void merge(void *out, const void *pa, size_t na,
const void *pb, size_t nb, size_t elemsize,
int (*cmp)(const void *, const void *))
{
while (na != 0 || nb != 0) {
if (na == 0 || nb != 0 && cmp(pa, pb) > 0) {
memcpy(out, pb, elemsize);
pb = (const char *)pb + elemsize;
nb--;
} else {
memcpy(out, pa, elemsize);
pa = (const char *)pa + elemsize;
na--;
}
out = (char *)out + elemsize;
}
}
/* Merge sort an array. */
void merge_sort(void *base, size_t nmemb, size_t elemsize,
int (*cmp)(const void *, const void *))
{
size_t nbottom;
size_t ntop;
void *midp;
void *bottom;
void *top;
if (nmemb <= 1) {
/* Too small to sort. */
return;
}
/* Sort the bottom half and the top half. */
nbottom = nmemb / 2;
ntop = nmemb - nbottom;
midp = (char *)base + (nbottom * elemsize);
merge_sort(base, nbottom, elemsize, cmp);
merge_sort(midp, ntop, elemsize, cmp);
/* Make temporary copies of the sorted bottom half and top half. */
bottom = malloc(nbottom * elemsize);
top = malloc(ntop * elemsize);
memcpy(bottom, base, nbottom * elemsize);
memcpy(top, midp, ntop * elemsize);
/* Do a sorted merge of the copies into the original. */
merge(base, bottom, nbottom, top, ntop, elemsize, cmp);
/* Free temporary copies. */
free(bottom);
free(top);
}

Sort an array in the relative order of elements of another array in c

I wish to sort a second array as per the first array. e.g.
first = {1,8,7,2,4}
second = {9,7,2,10,3}
I want first to be unchanged and second to be sorted in the same relative order as the first. i.e. the lowest value is at index 0, the second lowest value is at index 3, third lowest value is at index 4 etc etc
second = {2,10,9,3,7}
I have already tried some code for the following
#include <stdio.h>
typedef struct
{
int num;
int pos;
}ArrType;
ArrType arrA[5] = {{1,0},{8,1},{7,2},{2,3},{4,4}};
ArrType arrB[5] = {{9,0},{7,1},{2,2},{10,3},{3,4}};;
int cmparr(const void *a, const void *b)
{
ArrType *tmpa, *tmpb;
tmpa = (ArrType*) a;
tmpb = (ArrType*) b;
return(arrA[tmpa->pos].num - arrA[tmpb->pos].num);
}
int main(void)
{
int i;
qsort(arrB,5, sizeof(ArrType), cmparr);
for (i=0; i<5; i++)
{
printf ("%d ",arrB[i].num);
}
return (0);
}
The actual output is
9 10 3 2 7
I am open to a different data structure, but arrB should only be sorted one time.
I have seen some solutions for this in C++, Javascipt and other languages. But there is not a solution in C.
Edit - These arrays would be quite large in the final program. I am looking for a single sorting operation. i.e. single call to qsort
You need to create the meta-data that matches the desired ordering (i.e an array of indexes). Then apply that meta-data to the second array.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int first[] = {1,8,7,2,4};
int second[] = {9,7,2,10,3};
int compare(const void * a, const void * b);
int binary_search(int array[], int min, int max, int target);
void print_array(int * array, int c);
int main()
{
int idx;
int c = sizeof(first)/sizeof(int);
int * sorted = NULL;
int * indexes = NULL;
int * result = NULL;
if (NULL == (sorted = malloc(sizeof(first)))) {
return -1;
}
memcpy(sorted, first, sizeof(first));
if (NULL == (indexes = malloc(sizeof(first)))) {
free(sorted);
return -1;
}
memset(indexes, -1, sizeof(first));
if (NULL == (result = malloc(sizeof(second)))) {
free(sorted);
free(indexes);
return -1;
}
memset(result, -1, sizeof(second));
// 1st: Sort the reference array
qsort (sorted, c, sizeof(int), compare);
// 2nd: Record the position of each sorted element in the original array (this is your meta-data)
for (idx=0; idx<c; idx++) {
indexes[idx] = binary_search(sorted, 0, c, first[idx]);
}
// 3rd sort the target array
memcpy(sorted, second, sizeof(second));
qsort (sorted, c, sizeof(int), compare);
// 4th apply the stored positions to the sorted target array
for (idx = 0; idx < c; idx++) {
result[idx] = sorted[indexes[idx]];
}
print_array(result, c);
free(result);
free(indexes);
free(sorted);
return 0;
}
int compare(const void * a, const void * b)
{
return ( *(int*)a - *(int*)b );
}
int binary_search(int array[], int min, int max, int target)
{
int mid;
while (min <= max)
{
mid = min + (max - min)/2;
if (target > array[mid])
min = mid + 1;
else if (target < array[mid])
max = mid - 1;
else
return mid;
}
return -1;
}
void print_array(int * array, int c)
{
for(int i = 0; i < c; i++) {
printf("%d ", array[i]);
}
printf("\n");
}
Demo
Here is my approach, it uses qsort twice and arrC contains the result.
#include <stdio.h>
typedef struct
{
int num;
int pos;
}ArrType;
ArrType arrA[5] = {{1,0},{8,1},{7,2},{2,3},{4,4}};
int arrB[5] = {9,7,2,10,3};;
int arrC[5];
int cmpInt(const void *a, const void *b)
{
return(*a - *b);
}
int cmp(const void *a, const void *b)
{
ArrType *tmpa, *tmpb;
tmpa = (ArrType*) a;
tmpb = (ArrType*) b;
return(tmpa->num - tmpb->num);
}
int main(void)
{
int i;
qsort(arrA,5, sizeof(ArrType), cmp);
qsort(arrB,5, sizeof(ArrType), cmpInt);
for (i=0; i<5; i++)
{
arrC[arrA[i].pos] = arrB[i];
}
return (0);
}
Since C doesn't have a lambda compare (which could be used to sort an array of indexes according to first[]), the code below sorts an array of pointers ap[] to the elements of first[] using qsort(). Using pointers eliminates the need to pass an array name as a parameter for the compare function, which in turn allows the compare function to work with qsort(). The expression (ap[i]-first) converts a pointer into an index. Next second[] is sorted, also using qsort(). Then ap[] is used as a set of ranks to reorder second[] in place and in O(n) time.
To explain reorder by rank versus reorder by index:
dst[rank[i]] = src[i]; /* reorder by rank */
dst[i] = src[index[i]]; /* reorder by index */
Example code:
#include <memory.h>
#include <stdio.h>
#include <stdlib.h>
/* compare for ptr to integer */
int cmppi(const void *p0, const void *p1){
return (*(int *)p0 - *(int *)p1);
}
/* compare for ptr to ptr to integer */
int cmpppi(const void *p0, const void *p1){
return (**(int **)p0 - **(int **)p1);
}
int main()
{
int first[] = {1, 8, 7, 2, 4};
int second[] = {9, 7, 2,10, 3};
int **ap; /* array of pointers */
int *tmpp;
int tmpi;
size_t i, j;
/* allocate and generate array of pointers to first[] */
ap = (int **)malloc(sizeof(first)/sizeof(first[0])*sizeof(int *));
for(i = 0; i < sizeof(first)/sizeof(first[0]); i++)
ap[i] = &first[i];
/* sort ap */
qsort(ap, sizeof(first)/sizeof(first[0]), sizeof(int *), cmpppi);
/* sort second */
qsort(second, sizeof(second)/sizeof(second[0]), sizeof(int), cmppi);
/* reorder ap and second in place using ap as rank (O(n) time) */
for (i = 0; i < sizeof(second) / sizeof(second[0]); i++){
while(i != (j = ap[i] - first)){
tmpp = ap[i]; /* swap(ap[i], ap[j]) */
ap[i] = ap[j];
ap[j] = tmpp;
tmpi = second[i]; /* swap(second[i], second[j] */
second[i] = second[j];
second[j] = tmpi;
}
}
/* display second[] */
for (i = 0; i < sizeof(second) / sizeof(second[0]); i++)
printf("%3d", second[i]);
printf("\n");
free(ap);
return 0;
}

3 way quicksort (C implementation)

I try to implement some of the algorithms pure generic using C. I stick with the 3-way quicksort but somehow the implementation does not give correct output. The output nearly sorted but some keys aren't where it should be. The code is below. Thanks in advance.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
static void swap(void *x, void *y, size_t size) {
void *tmp = malloc(size);
memcpy(tmp, x, size);
memcpy(x, y, size);
memcpy(y, tmp, size);
free(tmp);
}
static int cmpDouble(const void *i, const void *j) {
if (*(double *)i < *(double *)j)
return 1;
else if (*(double *)i == *(double *)j)
return 0;
else
return -1;
}
void qsort3way(void *base, int lo, int hi, size_t size,
int (*cmp)(const void *, const void *)) {
if (hi <= lo)
return;
else {
char *ptr = (char*)base;
char *v = ptr + lo * size;
int lt = lo, gt = hi;
int i = lo;
while (i <= gt) {
int c = cmp(v, ptr + i * size);
if (c < 0)
swap(ptr + (lt++) * size, ptr + (i++) * size, size);
else if (c > 0)
swap(ptr + i * size, ptr + (gt--) * size, size);
else
i++;
}
qsort3way(base, lo, lt - 1, size, cmp);
qsort3way(base, gt + 1, hi, size, cmp);
}
}
int main(void) {
int i;
double *d = (double*)malloc(sizeof(double) * 100);
for (i = 0; i < 100; i++)
d[i] = (double)rand();
qsort3way(d, 0, 100 -1, sizeof(double), cmpDouble);
for (i = 0; i < 100; i++)
printf("%.10lf\n", d[i]);
free(d);
return 0;
}
sample output:
41.0000000000
153.0000000000
288.0000000000
2082.0000000000
292.0000000000
1869.0000000000
491.0000000000
778.0000000000
1842.0000000000
6334.0000000000
2995.0000000000
8723.0000000000
3035.0000000000
3548.0000000000
4827.0000000000
3902.0000000000
4664.0000000000
5436.0000000000
4966.0000000000
5537.0000000000
5447.0000000000
7376.0000000000
5705.0000000000
6729.0000000000
6868.0000000000
7711.0000000000
9961.0000000000
8942.0000000000
9894.0000000000
9040.0000000000
9741.0000000000
After reading the book link that you provide to #JohnBollinger. I understand how your algorithm work. Your problem is that your pivot move, but you don't change the value of v. Your pivot is at the index lt
char *ptr = base;
int lt = lo, gt = hi; // lt is the pivot
int i = lo + 1; // we don't compare pivot with itself
while (i <= gt) {
int c = cmp(ptr + lt * size, ptr + i * size);
if (c < 0) {
swap(ptr + lt++ * size, ptr + i++ * size, size);
}
else if (c > 0)
swap(ptr + i * size, ptr + gt-- * size, size);
else
i++;
}
qsort3way(base, lo, lt - 1, size, cmp);
qsort3way(base, gt + 1, hi, size, cmp);
I propose you a "proper" solution:
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
typedef void qsort3way_swap(void *a, void *b);
typedef int qsort3way_cmp(void const *a, void const *b);
static void qsort3way_aux(char *array_begin, char *array_end, size_t size,
qsort3way_cmp *cmp, qsort3way_swap *swap) {
if (array_begin < array_end) {
char *i = array_begin + size;
char *lower = array_begin;
char *greater = array_end;
while (i < greater) {
int ret = cmp(lower, i);
if (ret < 0) {
swap(i, lower);
i += size;
lower += size;
} else if (ret > 0) {
greater -= size;
swap(i, greater);
} else {
i += size;
}
}
qsort3way_aux(array_begin, lower, size, cmp, swap);
qsort3way_aux(greater, array_end, size, cmp, swap);
}
}
static void qsort3way(void *array_begin, void *array_end, size_t size,
qsort3way_cmp *cmp, qsort3way_swap *swap) {
qsort3way_aux(array_begin, array_end, size, cmp, swap);
}
static void swap_int_aux(int *a, int *b) {
int tmp = *a;
*a = *b;
*b = tmp;
}
static void swap_int(void *a, void *b) { swap_int_aux(a, b); }
static int cmp_int_aux(int const *a, int const *b) {
if (*a < *b) {
return 1;
} else if (*a > *b) {
return -1;
} else {
return 0;
}
}
static int cmp_int(void const *a, void const *b) { return cmp_int_aux(a, b); }
static void print_int(char const *intro, int const *array, size_t const size) {
printf("%s:", intro);
for (size_t i = 0; i < size; i++) {
printf(" %d", array[i]);
}
printf("\n");
}
#define SIZE 42
int main(void) {
int array[SIZE];
srand((unsigned int)time(NULL));
for (size_t i = 0; i < SIZE; i++) {
array[i] = rand() % SIZE - SIZE / 2;
}
print_int("before", array, SIZE);
qsort3way(array, array + SIZE, sizeof *array, cmp_int, swap_int);
print_int("after", array, SIZE);
}
Note: The optimization int i = lo + 1; and char *i = array_begin + size; are mandatory. Because in the case where the function compare return that pivot != pivot this will lead to a infinite recursion. How this would be possible?
The function cmp is bug.
double has strange power... A double can be not equal to itself! (-nan).
The implementation does not give the correct result because it is wrong. Pretty badly wrong, in fact, given that it's supposed to be a three-way quicksort and not a regular one.
One basic problem is that you've omitted the bit where you move the pivot(s) into their proper position after the main partitioning loop. For standard quicksort, that requires one extra swap or assignment after the loop, depending on implementation details. For a three-way quicksort that involves one or two extra loops to move the potentially-many values equal to the pivot into their positions.
A more insidious problem is the one #Stargateur first pointed out: you track the pivot element by pointer, not by value, and you (sometimes) swap the original value out from that position in the course of the partitioning loop.
Furthermore, your main partitioning loop is wrong for a three-way quicksort, too. When you encounter an element equal to the pivot you just leave it in place, but you need instead to move it to one end or the other (or to some kind of auxiliary storage, if you're willing to incur that memory cost) so that you can perform that move to the middle at the end. In a sense, the previous problem is a special case of this one -- you're not reserving space for or tracking the pivot values. Fixing this will solve the previous problem as well.
I'm not sure what reference you used to prepare your implementation, or whether you built it from scratch, but Geeks for Geeks has a C++ (but pretty much also C) implementation for int arrays that you might want to check out.
Your implementation is incorrect because the pivot may move during the partitioning phase and you use a pointer for the comparison which no longer points to it. Implementations in other languages use the value of the pivot instead of its address.
Note also these shortcomings:
recursing both ways may cause stack overflow on pathological distributions. In you case, an array that is already sorted is a pathological distribution.
the comparison function should return the opposite values: -1 if a < b, +1 is a > b and 0 if a == b.
the API is non-standard and confusing: you should pass the number of elements instead of a range with included bounds.
Here is a corrected and commented version:
#include <stdio.h>
#include <stdlib.h>
static void swap(unsigned char *x, unsigned char *y, size_t size) {
/* sub-optimal, but better than malloc */
while (size-- > 0) {
unsigned char c = *x;
*x++ = *y;
*y++ = c;
}
}
void qsort3way(void *base, int n, size_t size,
int (*cmp)(const void *, const void *))
{
unsigned char *ptr = (unsigned char *)base;
while (n > 1) {
/* use first element as pivot, pointed to by lt */
int i = 1, lt = 0, gt = n;
while (i < gt) {
int c = cmp(ptr + lt * size, ptr + i * size);
if (c > 0) {
/* move smaller element before the pivot range */
swap(ptr + lt * size, ptr + i * size, size);
lt++;
i++;
} else if (c < 0) {
/* move larger element to the end */
gt--;
swap(ptr + i * size, ptr + gt * size, size);
/* test with that element again */
} else {
/* leave identical element alone */
i++;
}
}
/* array has 3 parts:
* from 0 to lt excluded: elements smaller than pivot
* from lt to gt excluded: elements identical to pivot
* from gt to n excluded: elements greater than pivot
*/
/* recurse on smaller part, loop on larger to minimize
stack use for pathological distributions */
if (lt < n - gt) {
qsort3way(ptr, lt, size, cmp);
ptr += gt * size;
n -= gt;
} else {
qsort3way(ptr + gt * size, n - gt, size, cmp);
n = lt;
}
}
}
static int cmp_double(const void *i, const void *j) {
/* this comparison function does not handle NaNs */
if (*(const double *)i < *(const double *)j)
return -1;
if (*(const double *)i > *(const double *)j)
return +1;
else
return 0;
}
int main(void) {
double d[100];
int i;
for (i = 0; i < 100; i++)
d[i] = rand() / ((double)RAND_MAX + 1);
qsort3way(d, 100, sizeof(*d), cmp_double);
for (i = 0; i < 100; i++)
printf("%.10lf\n", d[i]);
return 0;
}

Move all values inside vectors in C

i'm writing a code for a project, in which i need to move values inside a vector! For example, i have a completely "null" vector:
[0][0][0][0][0]
Then i will add a value to it's first position: [10][0][0][0][0]!
Ok, now i need to add again a value to the first position, but i cant lose the value already there, i want to move it to the right, like:
[0][10][0][0][0]
To put the new one there: [5][10][0][0][0]
And then do this again, move the 5 and the 10 to the right again, to add a new one in the first, and so on!
I hope i was clear enough!
#EDIT :
Its done guys! Thanks to everyone that tried to help!
The problem was solved using #AkashPradhan function:
void changeposition(int* vetor, unsigned int size, int valor)
{
unsigned int i = size - 1;
for(; i > 0; --i)
vetor[i] = vetor[i-1] ;
vetor[0] = valor ;
}
Thanks #AkashPradhan! The function worked perfectly with my code!
You can use a Linked List and add to the front node or you could use an array and shift the elements by one position for each insertion.
In this case, I would recommend using a Linked List as the insertion routine will not involving moving every element. However if frequent random access to elements in required, use the array approach.
void insert_front(int* array, unsigned int size, int value)
{
unsigned int i = size - 1;
for(; i > 0; --i)
array[i] = array[i-1] ;
array[0] = value ;
}
Note that the routine does not check if you are inserting more elements that the size of the array.
You could implement this as a ring buffer, in which case you don't need to copy the elements all the time. An example of it would be:
typedef struct
{
size_t bufferSize;
size_t currentPos;
int *data;
} ring_buffer;
void init_buffer(ring_buffer *buffer, size_t N)
{
buffer->bufferSize = N;
buffer->currentPos = 0;
buffer->data = malloc(N*sizeof(int));
}
void free_buffer(ring_buffer *buffer)
{
free(buffer->data);
}
void push_buffer(ring_buffer *buffer, int value)
{
++buffer->currentPos;
if (buffer->currentPos >= buffer->bufferSize)
buffer->currentPos = 0;
buffer->data[buffer->currentPos] = value;
}
int get_buffer(ring_buffer *buffer, size_t pos)
{
size_t dataPos = buffer->currentPos + pos;
if (dataPos >= buffer->bufferSize)
dataPos -= buffer->bufferSize;
return buffer->data[dataPos];
}
or you could use a linked list as suggested by #AkashPradhan.
Define your own array, implement push operation, shift right data when push a new element. This implementation would delete the stale element.
typedef struct Array {
int *data;
size_t len; // current size of the array
size_t cap; // capacity of the array
}Array;
Array * new_array (size_t cap_){
Array *arr = (Array *)malloc(sizeof(Array));
arr->cap = cap_;
arr->len = 0;
arr->data = (int*)malloc(sizeof(int) * cap);
return arr;
}
void free_array(Array * arr){
if (arr->data != NULL)
free(arr->data);
free(arr);
}
void push(int x, Array *arr) {
int ptr = std::min(len, cap) - 2;
for(int i = ptr;i >= 0;i --) {
arr->data[ptr + 1] = arr->data[ptr];
}
arr->data[0] = x;
len = std::max(cap, len + 1);
}
Update : You can also use Tamás Szabó 's answer to avoid data movement.
If you need to write the corresponding function yourself then it can look the following way
size_t move( int a[], size_t n )
{
if ( n++ != 0 )
{
for ( size_t i = n; --i != 0; ) a[i] = a[i-1];
}
return n;
}
And you could use it the following way
size_t n = 0;
n = move( a, n );
a[0] = 10;
n = move( a, n );
a[0] = 5;
for ( size_t i = 0; i < n; i++ ) printf( "%d ", a[i] );
printf( "\n" );
Otherwise you have to use standard C function memmove the following way
memmove( a + 1, a, n * sizeof( int ) );
++n;
where n is the current number of actual elements in the array.

Resources