Quicksort implementation in c - c

I have a quicksort program which executes as shown. But the last element is not getting sorted. Can anyone tell me how to modify the program to obtain the correct result.
#include <stdlib.h>
#include <stdio.h>
static void swap(void *x, void *y, size_t l)
{
char *a = x, *b = y, c;
while(l--)
{
c = *a;
*a++ = *b;
*b++ = c;
}
}
static void sort(char *array, size_t size, int (*cmp)(void*,void*), int begin, int end)
{
if (end > begin)
{
void *pivot = array + begin;
int l = begin + size;
int r = end;
while(l < r)
{
if (cmp(array+l,pivot) <= 0)
{
l += size;
}
else if ( cmp(array+r, pivot) > 0 )
{
r -= size;
}
else if ( l < r )
{
swap(array+l, array+r, size);
}
}
l -= size;
swap(array+begin, array+l, size);
sort(array, size, cmp, begin, l);
sort(array, size, cmp, r, end);
}
}
void qsort(void *array, size_t nitems, size_t size, int (*cmp)(void*,void*))
{
sort(array, size, cmp, 0, (nitems-1)*size);
}
typedef int type;
int type_cmp(void *a, void *b){ return (*(type*)a)-(*(type*)b); }
int main(void)
{ /* simple test case for type=int */
int num_list[]={5,4,3,2,1};
int len=sizeof(num_list)/sizeof(type);
char *sep="";
int i;
qsort(num_list,len,sizeof(type),type_cmp);
printf("sorted_num_list={");
for(i=0; i<len; i++){
printf("%s%d",sep,num_list[i]);
sep=", ";
}
printf("};\n");
return 0;
}
Result:
sorted_num_list={2, 3, 4, 5, 1};
Why?
I tried doing (nitems)*size. But when I try the same for strings, it gives me error

The problem comes from this line:
sort(array, size, cmp, 0, (nitems-1)*size);
nitems is your number of items, here 5, you're substracting one so your quick sort will only sort the 4 first elements. Remove the -1 and it will work.
sort(array, size, cmp, 0, nitems*size);

2 mistakes here...
First change qsort() function names to qsort_user() or any other name then the qsort() because qsort() is the standard function defined in stdlib.h
Then change this line
sort(array, size, cmp, 0, (nitems-1)*size);
to
sort(array, size, cmp, 0, (nitems)*size);

Related

Generic Quicksort in C [closed]

Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 10 months ago.
Improve this question
Can anyone tell me what am I doing wrong in this generic quicksort code following this pseudocode Quicksort & Partition, the algorithm works, because I have already done it with integers only without the compare function by passing an int array to the quicksort and partition functions, but I have tried to make it work for both int and strings. In this code I have tested only the int values, but the code doesn't work, the output is the initial value of the array, it's the same exact thing for the strings I get the same initial array as an output. I have commented the string part because they get sorted the same way as the integers. This is the integer code that works Integer working code:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
//prototipi delle funzioni
typedef int (*compare_function)(const void *, const void *);
void generic_quicksort(void *v, int i, int f, size_t size, compare_function compare);
void generic_swap(void *a, void *b, size_t size);
int generic_partition(void *v, int i, int f, size_t size, compare_function compare);
void print_int_array(const int *array, size_t len) {
size_t i;
for (i = 0; i < len; i++)
printf("%d | ", array[i]);
putchar('\n');
}
//funzione di confronto
int compare_int(const void *, const void *);
int compare_str(const void *a, const void *b) {
const char **ia = (const char **)a;
const char **ib = (const char **)b;
return strcmp(*ia, *ib);
/* strcmp functions works exactly as expected from
comparison function */
}
void print_cstring_array(char **array, size_t len) {
size_t i;
for (i = 0; i < len; i++)
printf("%s | ", array[i]);
putchar('\n');
}
int main() {
int v[] = { 5, 4, 3, 2, 1 };
char *strings[] = { "Zorro", "Alex", "Celine", "Bill", "Forest", "Dexter" };
int n = sizeof(v) / sizeof(int);
print_int_array(v, n);
generic_quicksort((void *)v, 0, n - 1, sizeof(int), compare_int);
print_int_array(v, n);
/*
int s = sizeof(strings) / sizeof(*char);
print_cstring_array(strings, s);
generic_quicksort((void *)strings, 0, s - 1, sizeof(*char), compare_str);
print_cstring_array(strings, s);
*/
return 0;
}
int compare_int(const void *a, const void *b) {
return *((int*)a) - *((int*)b);
}
void generic_quicksort(void *v, int i, int f, size_t size, int (*comp)(const void *, const void *)) {
if (i >= f)
return;
int p = generic_partition(v, i, f, size, comp);
generic_quicksort(v, i, p - 1, size, comp);
generic_quicksort(v, p + 1, f, size, comp);
}
void generic_swap(void *a, void *b, size_t size) {
void *tmp = malloc(size);
memcpy(tmp, a, size);
memcpy(a, b, size);
memcpy(b, tmp, size);
free(tmp);
}
int generic_partition(void *v, int i, int f, size_t size, int (*comp)(const void *, const void *)) {
void *x = malloc(size);
int k, j;
memcpy(x, v + (i * size), size);
k = i - 1;
for (j = i; j <= f - 1; j++) {
if (comp(v + (j * size), x) <= 0) {
k++;
generic_swap(v + (k * size), v + (j * size), size);
}
}
generic_swap(v + ((k + 1) * size), v + (f * size), size);
free(x);
return (k + 1);
}
There are multiple problems in the code:
int n = sizeof(v) / sizeof(int); is risky: there is a silent assumption about the type of v. You should write int n = sizeof(v) / sizeof(*v);
The convention to pass the indices of the first and last elements of the slice is confusing and not idiomatic in C, you should pass the index of the first element and the index of the element after the last one. This allows for unsigned index types and empty arrays.
v + (j * size) uses void pointer arithmetics, which is an extension not available on all systems. Use unsigned char pointers for this.
the comparison function for integers has undefined behavior for large absolute values because subtracting them may cause an arithmetic overflow. You should use this instead:
int compare_int(const void *a, const void *b) {
int ia = *(const int *)a;
int ib = *(const int *)b;
return (ia > ib) - (ia < ib);
}
generic_swap uses malloc and memcpy. This causes much overhead for small elements, you should use a simple loop:
void generic_swap(void *a, void *b, size_t size) {
unsigned char *pa = (unsigned char *)a;
unsigned char *pb = (unsigned char *)b;
while (size-- > 0) {
unsigned char c = *pa;
*pa++ = *pb;
*pb++ = c;
}
}
The generic_partition in the reference uses the last element as the pivot, but you initialize x from the first element. You should write memcpy(x, v + (f * size), size);. This is causing the failure. The current code might work by coincidence for the int version. Using the first or the last element as a pivot causes worst case behavior on sorted arrays.
Here is a modified version:
#include <stdio.h>
#include <string.h>
//prototipi delle funzioni
typedef int (*compare_function)(const void *, const void *);
void generic_quicksort(void *v, int i, int f, size_t size, compare_function compare);
//funzione di confronto
int compare_int(const void *a, const void *b) {
int ia = *(const int *)a;
int ib = *(const int *)b;
return (ia > ib) - (ia < ib);
}
int compare_str(const void *a, const void *b) {
const char *sa = *(const char * const *)a;
const char *sb = *(const char * const *)b;
return strcmp(sa, sb);
}
void print_int_array(const int *array, size_t len) {
size_t i;
if (len > 0) {
printf("%d", array[0]);
for (i = 1; i < len; i++)
printf("| %d", array[i]);
}
putchar('\n');
}
void print_cstring_array(const char * const *array, size_t len) {
size_t i;
if (len > 0) {
printf("%s", array[0]);
for (i = 1; i < len; i++)
printf(" | %s", array[i]);
}
putchar('\n');
}
static void generic_swap(void *a, void *b, size_t size) {
unsigned char *pa = (unsigned char *)a;
unsigned char *pb = (unsigned char *)b;
while (size-- > 0) {
unsigned char c = *pa;
*pa++ = *pb;
*pb++ = c;
}
}
static int generic_partition(void *v, int i, int f, size_t size,
int (*comp)(const void *, const void *))
{
unsigned char *p = (unsigned char *)v;
int j, k = i;
// using first element as pivot
for (j = i + 1; j < f; j++) {
if (comp(p + j * size, p + i * size) <= 0) {
k++;
generic_swap(p + k * size, p + j * size, size);
}
}
/* swap the pivot to the end of the left part */
generic_swap(p + i * size, p + k * size, size);
return k;
}
void generic_quicksort(void *v, int i, int f, size_t size,
int (*comp)(const void *, const void *))
{
if (f > i + 1) {
int p = generic_partition(v, i, f, size, comp);
generic_quicksort(v, i, p, size, comp);
generic_quicksort(v, p + 1, f, size, comp);
}
}
int main() {
int v[] = { 5, 4, 3, 2, 1 };
int n = sizeof(v) / sizeof(*v);
const char *strings[] = { "Zorro", "Alex", "Celine", "Bill", "Forest", "Dexter" };
int s = sizeof(strings) / sizeof(*strings);
print_int_array(v, n);
generic_quicksort((void *)v, 0, n, sizeof(*v), compare_int);
print_int_array(v, n);
print_cstring_array(strings, s);
generic_quicksort((void *)strings, 0, s, sizeof(*strings), compare_str);
print_cstring_array(strings, s);
return 0;
}
Note that choosing the first or the last element as the pivot leads to worst case complexity for a sorted array. The depth of recursion for generic_quicksort will be the length of the array, potentially causing a stack overflow.
Here is a modified version that is protected against this, but still has quadratic time complexity on a sorted array:
void generic_quicksort(void *v, int i, int f, size_t size,
int (*comp)(const void *, const void *))
{
while (f > i + 1) {
int p = generic_partition(v, i, f, size, comp);
if (p - i < f - p) {
generic_quicksort(v, i, p, size, comp);
i = p + 1;
} else {
generic_quicksort(v, p + 1, f, size, comp);
f = p;
}
}
}

Strange issue with merge sort, quick sort, and structs in C

While doing my C programming exercises, I've encountered this strange issue:
merge sort and quick sort algorithms loop infinitely through my array of structs, trying to sort it.
Now, there is a third sorting algorithm available: insertion sort. With this, the sorting works fine.
So, I tested all 3 algorithms before doing this exercise, and they work fine (tried with int, double, strings and array of strings...).
I have no idea about that... Any suggestion?
This is the code of merge sort:
void upo_merge_sort(void *base, size_t n, size_t size, upo_sort_comparator_t cmp)
{
assert(base != NULL);
upo_merge_sort_rec(base, 0, n-1, size, cmp);
}
void upo_merge_sort_rec(void *base, size_t lo, size_t hi, size_t size, upo_sort_comparator_t cmp)
{
if(lo >= hi) { return; }
size_t mid = lo + (hi - lo) / 2;
upo_merge_sort_rec(base, 0, mid, size, cmp);
upo_merge_sort_rec(base, mid+1, hi, size, cmp);
upo_merge_sort_merge(base, lo, mid, hi, size, cmp);
}
void upo_merge_sort_merge(void *base, size_t lo, size_t mid, size_t hi, size_t size, upo_sort_comparator_t cmp)
{
unsigned char *ptr = base;
unsigned char *aux = NULL;
size_t n = hi - lo + 1;
size_t i = 0;
size_t j = mid + 1 - lo;
size_t k;
aux = malloc(n*size);
if(aux == NULL) {
perror("Unable to allocate memory for auxiliary vector");
abort();
}
memcpy(aux, ptr+lo*size, n*size);
for(k = lo; k <= hi; ++k) {
if(i > (mid - lo)) {
memcpy(ptr+k*size, aux+j*size, size);
++j;
}
else if(j > (hi - lo)) {
memcpy(ptr+k*size, aux+i*size, size);
++i;
}
else if(cmp(aux+j*size, aux+i*size) < 0) {
memcpy(ptr+k*size, aux+j*size, size);
++j;
}
else {
memcpy(ptr+k*size, aux+i*size, size);
++i;
}
}
free(aux);
}
and compare functions:
int by_track_number_comparator(const void *a, const void *b)
{
const entry_t *aa = a;
const entry_t *bb = b;
int diff = aa->track_num - bb->track_num;
return diff;
}
int by_track_title_comparator(const void *a, const void *b)
{
const entry_t *aa = a;
const entry_t *bb = b;
return strcmp(aa->track_title, bb->track_title);
}
entry_t is a struct type.
There is a simple error in upo_merge_sort_rec that makes the function recurse far deeper than it needs to. It should be sorting the elements from index lo to hi inclusive, but the lower index of one of the recursive calls is incorrectly using a fixed lower index of 0:
upo_merge_sort_rec(base, 0, mid, size, cmp);
should be:
upo_merge_sort_rec(base, lo, mid, size, cmp);

Segmentation fault on recursive merge sort

I'm trying to write a fully recursive version of merge sort
I get segmentation fault on merge function when I try to order more than 100k records, I read online that it would be a stack overflow issue, related to VLAs, but I can't figure it out.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct _MBArray MBArray;
struct _MBArray {
void ** array;
int length;
};
void mergeSort(MBArray *mbarray, int start, int end, int (*compare)(void *, void *)) {
if (start == end) {
return;
} else {
int middle = (end + start) / 2;
mergeSort(mbarray, start, middle, compare);
mergeSort(mbarray, middle + 1, end, compare);
merge(mbarray, start, middle + 1, end, compare, (mbarray->array)[middle + 1]);
return;
}
}
void merge(MBArray *mbarray, int startA, int startB, int length, int (*compare)(void *, void *),void *elem) {
if (startA > startB - 1) {
return;
} else
if (startB > length) {
return;
} else
if ((*(compare))((mbarray->array)[startA], (mbarray->array)[startB])) {
merge(mbarray, startA + 1, startB, length, compare, (mbarray->array)[startB]);
return;
} else {
memcpy(mbarray->array + (startA + 1), mbarray->array + startA, (startB - startA) * sizeof(void *));
(mbarray->array)[startA] = elem;
merge(mbarray, startA + 1, startB + 1, length, compare, (mbarray->array)[startB + 1]);
return;
}
}
Your merge and mergesort functions are incorrect:
the mergesort function is given index values into the array such that start is included and end is excluded. Here is how to fix it:
void mergeSort(MBArray *mbarray, int start, int end,
int (*compare)(void *, void *))
{
if (end - start > 1) {
int middle = start + (end - start) / 2;
mergeSort(mbarray, start, middle, compare);
mergeSort(mbarray, middle, end, compare);
merge(mbarray, start, middle, end, compare);
}
}
The merge function should be simplified. If you really intend for this function to recurse instead of using a temporary array, you might recurse as many as length/2 times hence are likely to cause a stack overflow even faster than by allocating a VLA in the merge function.
Here is a VLA based merge function:
void merge(MBArray *mbarray, int low, int middle, int end,
int (*compare)(void *, void *))
{
int templen = middle - low;
void *temp[templen];
memcpy(temp, mbarray->array + low, sizeof(temp));
int i = 0, j = middle, k = low;
while (i < templen) {
if (j >= end || compare(temp[i], mbarray->array[j])) {
mbarray->array[k++] = temp[i++];
else
mbarray->array[k++] = mbarray->array[j++];
}
}
A fully recursive version should save the element and recurse before setting it into its final position. This is much more costly in stack space than allocating a VLA so stack overflow is more likely. Allocating a temporary array once with malloc() and passing it to mergesort is much safer and also more efficient.
Here is a modified fully recursive merge function:
void mergeRecur(MBArray *mbarray, int dest, int a0, int a1, int b0, int b1,
int (*compare)(void *, void *))
{
if (a0 < a1) {
if (b0 >= b1 || compare(mbarray->array[a0], mbarray->array[b0])) {
void *temp = mbarray->array[a0];
mergeRecur(mbarray, dest + 1, a0 + 1, a1, b0, b1, compare);
mbarray->array[dest] = temp;
} else {
void *temp = mbarray->array[b0];
mergeRecur(mbarray, dest + 1, a0, a1, b0 + 1, b1, compare);
mbarray->array[dest] = temp;
}
}
}
void merge(MBArray *mbarray, int low, int middle, int end,
int (*compare)(void *, void *))
{
mergeRecur(mbarray, low, low, middle, middle, end, compare);
}
For large arrays, I would recommend allocating a temporary array once and calling a recursive version of mergesort that uses the temporary array instead of allocating a VLA:
#include <stdlib.h>
#include <string.h>
struct _MBArray {
void ** array;
int length;
};
void mergeTemp(MBArray *mbarray, int low, int middle, int end,
int (*compare)(void *, void *), void **temp)
{
int templen = middle - low;
int i = 0, j = middle, k = low;
memcpy(temp, mbarray->array + low, sizeof(*temp) * templen);
while (i < templen) {
if (j >= end || compare(temp[i], mbarray->array[j])) {
mbarray->array[k++] = temp[i++];
else
mbarray->array[k++] = mbarray->array[j++];
}
}
void mergeSortTemp(MBArray *mbarray, int start, int end,
int (*compare)(void *, void *), void **temp)
{
if (end - start > 1) {
int middle = start + (end - start) / 2;
mergeSortTemp(mbarray, start, middle, compare, temp);
mergeSortTemp(mbarray, middle, end, compare, temp);
mergeTemp(mbarray, start, middle, end, compare, temp);
}
}
void mergeSort(MBArray *mbarray, int (*compare)(void *, void *)) {
if (mbarray->length > 1) {
void **temp = malloc(sizeof(*temp) * (mbarray->length / 2));
mergeSortTemp(mbarray, 0, mbarray->length, compare, temp);
free(temp);
}
}
The problem is most likely due to merge() calling itself for each element merged. It would be better to have an entry function for mergeSort() that does a one time allocation of a second array (of pointers), then pass the second array as a parameter to a recursive MergeSortR(), and merge back and forth between the two arrays.

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;
}

How to properly use qsort()?

I am trying to write a simple program to find the median of an array. To do that first I tried to use qsort to put my values in order but I can't quite get it to work. I believe it has something to do with my function parameter and pointers.
#include <stdio.h>
#include <stdlib.h>
int cmpfunc (const void * a, const void * b)
{
return ( *(int*)a - *(int*)b );
}
int median(const int* array)
{
double median;
int length =0;
while(array[length])
length++;
qsort(*array, length, sizeof(int), cmpfunc);
if(length %2 ==0)
{
median=(array[length/2] + array[(length/2)+1])/2;
}
else
{
median = array[(length +1/2)];
}
return median;
}
int main ()
{
int array [] = {5, 3, 2, 7, 9};
printf( "%f", median(array));
}
Beside that you are getting wrong value for length with snippet
while(array[length]) // Array is not ended with zero
length++; // Length would get some unexpected value
you need to return double from function median and it also need one more parameter for length.
double median(int* array, const int length){...}
// ^Removed const qualifier and added an extra parameter for array size
Call median as
double result = median(array, sizeof(array)/sizeof(array[0]))
From the qsort documentation:
void qsort(void *base, size_t nmemb, size_t size,
int (*compar)(const void *, const void *));
The qsort() function sorts an array with nmemb elements of size size.
The base argument points to the start of the array.
First you need to get the length right; second, in your original call to qsort you used qsort(*array...) which is not what you want. Just use qsort(array...).
It is also useful when writing these small tests to view the actual data; you can either use a debugger, or just print out the contents of your array.
#include <stdio.h>
#include <stdlib.h>
int cmpfunc(const void *a, const void *b)
{
return (*(int *) a - *(int *) b);
}
void printArray(int *array, int length)
{
int i;
for (i = 0; i < length; i++) {
printf("%d ", array[i]);
}
printf("\n");
}
int median(int *array, int length)
{
double median;
printArray(array, length);
qsort(array, length, sizeof(int), cmpfunc);
printArray(array, length);
if (length % 2 == 0) {
median = (array[length / 2] + array[(length / 2) + 1]) / 2;
} else {
median = array[(length + 1 / 2)];
}
return median;
}
int main()
{
int array[] = { 5, 3, 2, 7, 9 };
printf("%d\n", median(array, sizeof(array) / sizeof(array[0])));
return 0;
}

Resources