I decided for educational purpose to make a generic sort like the one in the standard library as an exercise for my self in learning more about how to do generic programming in C and so I would really appreciate it if you guys could tell me how I could have done a better job and what other various improvements I could add to make my code better. I have quite a bit of functions to go through so I'm going to explain what each function does as much as I need to.
Main
int main()
{
/*
Throughout this source code, if you see an unsigned int, more than likely
I'm using an unsigned int because I'm comparing to a variable of type size_t
*/
unsigned int i;
int a[] = {200, 1, 99, 23, 56, 207, 369, 136, 147, 21, 4, 75, 36, 12};
size_t numberOfElements = sizeof(a)/sizeof(a[0]);
/*
No point in performing quick sort if it does not have more than 1 elements and if
the array is sorted.
*/
if(numberOfElements > 1 && !isSorted(a, numberOfElements, sizeof(int), cmp))
{
quickSort_h(a, numberOfElements, sizeof(int), cmp);
}
for(i = 0; i < numberOfElements; i++)
{
printf("%d ", a[i]);
}
return 0;
}
Quick Sort Helper
void quickSort_h(void *base, size_t nitems, size_t memSize, int (*cmp)(const void *, const void *))
{
quickSort(base, 0, nitems - 1, memSize, cmp);
insertionSort(base, nitems, memSize, cmp);
}
Quick Sort
/*
Here we are doing a tail recurse optimization and using our threshold value(20) to know
when to move to Insertion Sort.
*/
void quickSort(void *base, int first, int last, size_t memSize, int (*cmp)(const void *, const void *))
{
while(last - first > THRESHOLD)
{
int pivot;
pivot = qSortPartition(base, first, last, memSize, cmp);
/*
Here we are checking which is easier to recurse by checking if the subarray
on the left is shorter than the one on the right and vice versa.
*/
if(pivot - first < last - pivot)
{
quickSort(base, first, pivot - 1, memSize, cmp);
first = pivot + 1;
}
else
{
quickSort(base, pivot + 1, last, memSize, cmp);
last = pivot - 1;
}
}
}
qSortPartition
/*
Here we find the median of the first, middle and last element and we will use
that as are starting pivot.
*/
int qSortPartition(void *base, int first, int last, size_t memSize, int (*cmp)(const void *, const void *))
{
char *carray = (char *)base;
int pivot, mid = first + (last - first) / 2;
// Find the larger of the two
pivot = cmp(&carray[first * memSize], &carray[mid * memSize]) > 0 ? first : mid;
// Find the smallest one.
if(cmp(&carray[pivot * memSize], &carray[last * memSize]) > 0)
{
pivot = last;
}
// Put the pivot at the front of the list.
byteSwap(&carray[pivot * memSize], &carray[first * memSize], memSize);
pivot = first;
while(first < last)
{
/*
if the value we have is less than the element at the end, move the pivot up
by 1, else move first.
*/
if(cmp(&carray[first * memSize], &carray[last * memSize]) <= 0)
{
byteSwap(&carray[pivot * memSize], &carray[first * memSize], memSize);
pivot++;
}
first++;
}
/* finally swap the pivot with the last element. At this point, all elements to the
left of the pivot are less than it and vice versa.
*/
byteSwap(&carray[pivot * memSize], &carray[last * memSize], memSize);
return pivot;
}
Insertion Sort
void insertionSort(void *base, size_t nitems, size_t memSize, int (*cmp)(const void *, const void *))
{
char *carray = (char *)base;
unsigned int i;
int j;
for(i = 0; i < nitems; i++)
{
j = i;
while(j > 0 && cmp(&carray[j * memSize], &carray[(j - 1) * memSize]) < 0)
{
byteSwap(&carray[j * memSize], &carray[(j - 1) * memSize], memSize);
j--;
}
}
}
isSorted
int isSorted(void *base, size_t nitems, size_t memSize, int (*cmp)(const void *, const void *))
{
char *carray = (char *)base;
unsigned int i;
for(i = 0; i < nitems - 1; i++)
{
// Simply check if the current element is greater than the next element.
if(cmp(&carray[i * memSize], &carray[(i + 1) * memSize]) > 0)
{
return 0;
}
}
return 1;
}
compare
int cmp(const void *a, const void *b)
{
const int *A = a, *B = b;
// A > B = 1, A < B = -1, (A == B) = 0
return (*A > *B) - (*A < *B);
}
byteSwap
/*
The main way that this function is swapping is by swapping out the bytes in each
iteration.
*/
void byteSwap(void *a, void *b, size_t memSize)
{
char tmp;
char *aa = a, *bb = b;
do
{
tmp = *aa;
*aa++ = *bb;
*bb++ = tmp;
}
while(--memSize > 0);
}
pivot = min(max(first,mid),last);
and it will return thelast
value not only if it's a median, but also when it is the smallest one among the three. – CiaPan Sep 15 '16 at 7:36