Related
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;
}
}
}
Example:
input:
Number of words user want to enter : 3
Word no. 1: cat
Word no. 2: rat
Word no. 3: dog
output:
cat rat dog
cat dog rat
rat cat dog
rat dog cat
dog cat rat
dog rat cat
something like this.
Here is the code of another program, where I arranged letters in different combinations.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
char character_swap(char*, char*);
void permutation(char*, int, int);
int main()
{
char str[99];
printf("Enter a string to find its permutations.\n");
scanf("%s",str);
system("clear");
int n = strlen(str);
permutation(str, 0, n-1);
return 0;
}
char character_swap(char *x, char *y)
{
char temp;
temp = *x;
*x = *y;
*y = temp;
}
void permutation(char *a, int l, int r)
{
if (l == r)
printf("%s\n", a);
else
for (int i = l; i <= r; i++)
{
character_swap((a+l), (a+i));
permutation(a, l+1, r);
character_swap((a+l), (a+i));
}
}
Can anybody tell me, how can I do the same thing with words?
You have done a good job separating the permutation implementation from the main interface, such that this can this be done fairly easily. First, there are a few inconsistencies,
In character_swap, one has declare return type char, but never
return anything; must be void. It's used only in this file, so it should be static; this will avoid namespace conflicts let the compiler perform more aggressive optimizations. It also prevents prototype warnings as long as it is declared above.
int is not really the right type for working with indices; size_t is more appropriate.
It's a little awkward to explain that permutation is always going to be called with 0 index as it's second parameter. It's better to drop that and fill it in later, I have accepted the size in character_permutation, which calls character_permutation_r.
I have generalized the type from char to type using a typedef; I just replaced char by type. It would be simpler and clearer to replace type by equivalent char *, but in the next iteration, we are going to replace it with a totally opaque type; a typedef is warranted it because it keeps them all the type information together.
#include <stdio.h>
#include <string.h>
/* This is part of the interface. */
void character_array_output(const char *a, const size_t size) {
(void)size; /* Don't use the size -- it's null-terminated. */
printf("%s\n", a);
}
/* This is the implementation. */
typedef char type;
typedef void (*character_array_action)(const type *, size_t);
static character_array_action character_print = &character_array_output;
static void character_swap(type *x, type *y)
{
type temp;
temp = *x;
*x = *y;
*y = temp;
}
static void character_permutation_r(type *a, const size_t l, const size_t r)
{
if (l == r)
character_print(a, r + 1);
else
for (size_t i = l; i <= r; i++)
{
character_swap((a+l), (a+i));
character_permutation_r(a, l+1, r);
character_swap((a+l), (a+i));
}
}
static void character_permutation(type *a, const size_t size) {
if(size == 0) return;
else character_permutation_r(a, 0, size - 1);
}
/* This is part of the interface. */
int main(void)
{
char str[99];
printf("Enter a string to find its permutations.\n");
scanf("%98s", str);
character_permutation(str, strlen(str));
return 0;
}
For your question, type is char, and you want it to be char *, you just replace it with typedef char *type. However, a generalization to any assignable type is possible.
#include <stdio.h>
#include <string.h>
/* This is the implementation.
`name` satisfies `C` naming conventions when mangled
`nametype` is an assignable type
`array_output` is a function satisfying `name##_array_action` */
#define DEFINE_PERMUTATION(name, nametype, array_output) \
typedef nametype name##type; \
\
typedef void (*name##_array_action)(const name##type *, size_t); \
\
static name##_array_action name##_array_print = &array_output; \
\
static void name##_swap(name##type *x, name##type *y) \
{ \
name##type temp; \
\
temp = *x; \
*x = *y; \
*y = temp; \
} \
\
static void name##_permutation_r(name##type *a, const size_t l, const size_t r) \
{ \
if (l == r) \
name##_array_print(a, r + 1); \
\
else\
for (size_t i = l; i <= r; i++)\
{ \
name##_swap((a+l), (a+i)); \
name##_permutation_r(a, l+1, r); \
name##_swap((a+l), (a+i)); \
} \
} \
\
static void name##_permutation(name##type *a, size_t size) { \
if(size == 0) return; \
else name##_permutation_r(a, 0, size - 1); \
}
/* This is the interface. */
void character_array_output(const char *a, const size_t a_size) {
(void)a_size;
printf("%s\n", a);
}
void string_array_output(char *const a[], const size_t a_size) {
for(size_t i = 0; i < a_size; i++)
printf("%s%s", i ? ", " : "", a[i]);
printf("\n");
}
void integer_array_output(const int *a, const size_t a_size) {
printf("{");
for(size_t i = 0; i < a_size; i++)
printf("%s%d", i ? ", " : "", a[i]);
printf("}\n");
}
typedef int (*operation)(int);
static int value;
void operation_array_output(const operation *a, const size_t a_size) {
printf("{");
for(size_t i = 0; i < a_size; i++)
printf("%s%d", i ? ", " : "", value = a[i](value));
printf("}\n");
}
DEFINE_PERMUTATION(character, char, character_array_output)
DEFINE_PERMUTATION(string, char *, string_array_output)
DEFINE_PERMUTATION(integer, int, integer_array_output)
DEFINE_PERMUTATION(operation, operation, operation_array_output)
#include <math.h>
static int square_n(const int n) { return n * n; }
static int sqrt_n(const int n) { return sqrtl(n); }
static int double_n(const int n) { return 2 * n; }
int main(void)
{
char str[] = "abc";
character_permutation(str, strlen(str));
char *strings[] = { "cat", "rat", "dog" };
string_permutation(strings, sizeof strings / sizeof *strings);
int numbers[] = { 42, 99, 1, 0 };
integer_permutation(numbers, sizeof numbers / sizeof *numbers);
operation ops[] = { &square_n, &sqrt_n, &double_n };
value = 1;
operation_permutation(ops, sizeof ops / sizeof *ops);
value = 5;
operation_permutation(ops, sizeof ops / sizeof *ops);
return 0;
}
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;
}
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;
}
Let's say I have structure like this:
typedef struct MyStruct{
char *string1;
int number1, number2, number3;
char string2[11], string3[9];
char *string4;
char *string5;
}MyStruct;
Programs prompts user to choose by what field it should sort the data. I am having trouble thinking of a way to sort array effectively. Do I really need to write separate sorting functions for each field? There must be some other way, because writing 8 functions, where 2 would suffice, doesn't look rational.
Look up qsort() from <stdlib.h>. It takes a comparator function. You can write separate comparator functions for the different sort orders, but still use the standard library qsort() to do the sorting.
For example:
int ms_cmp_string1(const void *vp1, const void *vp2)
{
const MyStruct *ms1 = vp1;
const MyStruct *ms2 = vp2;
int cmp = strcmp(ms1->string1, ms1->string2);
if (cmp != 0)
return cmp;
else if (ms1->number1 < ms2->number1)
return -1;
else if (ms1->number1 > ms2->number1)
return +1;
//...other comparisons as required...
else
return 0;
}
This is a decent outline for comparators. This one sorts on string1 and then by number1. You can either write variants that sort on different fields, or devise a scheme that applies the various possible tests in an order of your choosing. But the basic outline works pretty well and is suitable for passing to qsort() without any casts necessary.
You don't need to write 8 functions if only 2 are needed. Build your own qsort function and send a last parameter containing the member offset to the compare function, then, in your compare function, cast pointer + offset to the right type.
Something like:
int comp_int(const void *pa, const void *pb, size_t offset)
{
const int *a = (const int *)((const char *)pa + offset);
const int *b = (const int *)((const char *)pb + offset);
return *a - *b;
}
int comp_string(const void *pa, const void *pb, size_t offset)
{
const char *a = (const char *)pa + offset;
const char *b = (const char *)pb + offset;
return strcmp(a, b);
}
void swap(void *v[], int a, int b)
{
void *temp;
temp = v[a];
v[a] = v[b];
v[b] = temp;
}
void sort(void *v[], int left, int right, size_t offset, int (*comp)(const void *, const void *, size_t))
{
int i, last;
if (left >= right) return;
swap(v, left, (left + right) / 2);
last = left;
for (i = left + 1; i <= right; i++) {
if ((*comp)(v[i], v[left], offset) < 0)
swap(v, ++last, i);
}
swap(v, left, last);
sort(v, left, last - 1, offset, comp);
sort(v, last + 1, right, offset, comp);
}
offsetof can help
Here is a sample of using qsort from my another answer:
struct stringcase { char* string; void (*func)(void); };
void funcB1();
void funcAzA();
struct stringcase cases [] =
{ { "B1", funcB1 }
, { "AzA", funcAzA }
};
struct stringcase work_cases* = NULL;
int work_cases_cnt = 0;
// comparator function
int stringcase_cmp( const void *p1, const void *p2 )
{
return strcasecmp( ((struct stringcase*)p1)->string, ((struct stringcase*)p2)->string);
}
// prepare the data for searching
void prepare() {
// allocate the work_cases and copy cases values from it to work_cases
qsort( cases, i, sizeof( struct stringcase ), stringcase_cmp );
}
If you're using the GNU C library, there's an extension called qsort_r() that lets you pass an extra parameter to the comparison function.
Using some macros:
#include <stdio.h>
#include <stdlib.h>
struct data {
int x, y, z;
};
#define comp(member) comp_##member
#define comp_build(member) \
int comp_##member(const void *pa, const void *pb) \
{ \
const struct data *a = pa, *b = pb; \
return (a->member < b->member) ? -1 : (a->member > b->member); \
}
comp_build(x)
comp_build(y)
comp_build(z)
int main(void)
{
#define ROWS 3
struct data v[] = {
{3, 2, 1},
{1, 3, 2},
{2, 1, 3}
};
int i;
puts("Unsorted");
for (i = 0; i < ROWS; i++) printf("%d %d %d\n", v[i].x, v[i].y, v[i].z);
qsort(v, ROWS, sizeof(struct data), comp(x));
puts("Sorted by x");
for (i = 0; i < ROWS; i++) printf("%d %d %d\n", v[i].x, v[i].y, v[i].z);
puts("Sorted by y");
qsort(v, ROWS, sizeof(struct data), comp(y));
for (i = 0; i < ROWS; i++) printf("%d %d %d\n", v[i].x, v[i].y, v[i].z);
puts("Sorted by z");
qsort(v, ROWS, sizeof(struct data), comp(z));
for (i = 0; i < ROWS; i++) printf("%d %d %d\n", v[i].x, v[i].y, v[i].z);
return 0;
}