简体   繁体   中英

Counting swaps in Quicksort in C

I am trying to count the number of swaps that occur in my quicksort in C. However, I am getting values that are incorrect and not sure where I went wrong. I am using a structures as my arrays to be sorted.

struct anArray{
int numbers[maxSize];
int swaps;
};

/* Partition function */
int partition(struct anArray *array, int start, int end){
if(start == end){
    return start;
}
int pivot = array->numbers[end];
int low = start - 1;
int high = end;

for(;;){
    do{
        low++;
    } while(array->numbers[low] < pivot);

    do{
        high--;
    } while(array->numbers[high] > pivot);

    /* Detector for when the cells meet */
    if(low >= high){
        swap(array, low, end);
        return low;
    }
  }
/* Swapping the values */
swap(array, low, high);
}

This is my partition function used to "separate" the arrays.

void quickSort(struct anArray *array, int start, int end){
if(end - start <= 0){ return; }
else{
    int pivot = array->numbers[end];
    int partitionPoint = partition(array, start, end);

    quickSort(array, start, partitionPoint - 1);
    quickSort(array, partitionPoint + 1, end);
  }
}

This is my quicksorting function. It's a recursive function. My swap function increments counter by 1 every time it's called.

In my main, I set myArray->swaps = counter; But the number of times the swaps occurs isn't right. For example, if I sort an array that goes from 1 to 9, the number of swaps should be 0 but I get 9. I've tried incrementing counter when it's in the partition function only but it gives me the same result.

Is there something wrong with my partition function?

Thank you very much

Edit 1:

Here's my swap function.

void swap(struct anArray *array, int first, int second){
int temp = array->numbers[first];
array->numbers[first] = array->numbers[second];
array->numbers[second] = temp;
counter++;
}

I've tried using

void swap(struct anArray *array, int first, int second, int swapCount)

and then have swapCount be array->swaps when calling the swap function, and incrementing it by 1 but it gives me the same answer.

Here's a part of my main.

int main(){
  struct anArray *ascending = (struct anArray*)malloc(10 * sizeof(struct anArray));
  int ascend[maxSize] = {  1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
  initArray(ascending, ascend);

  quickSort(ascending, 0, maxSize - 1);
  ascending->swaps = counter;
  printf("Test: Unique random values\nSorted: [ ");
  for(int i = 0; i < maxSize; i++){
    printf("%i ", ascending->numbers[i]);
}
printf("]\nSwaps: %i\nComps: \n\n", ascending->swaps);

The other parts of my main are just other arrays to be sorted. The initArray is used to set the values of array->numbers and also reset array->swaps to 0.

Your quicksort code seems pretty good. I didn't examine it rigorously, but it passed a simple test, so I didn't investigate further. ( Edit: Based on your feedback, I created a third version in my second update that shows that the sort has an issue for larger data inputs).

The main bug was the malloc at the top of main . We do not want an array of the struct anArray :

struct anArray *ascending = malloc(10 * sizeof(struct anArray));

That is, we do not want (eg) 10 structs, we want a single struct and to fill in 10 int s that go into the numbers field that is in that single struct.

The initArray function was not posted, so I had to guess/deduce what it might be. Based on the above bug, I'm not sure that numbers would have been initialized correctly.

From the code fragments posted, I was able to piece together a whole program. I've created two versions:

One with the bugs annotated [but not fixed] that compiles cleanly.

And, a second that is fully cleaned up, working, and generalized for arbitrary array sizes [please pardon the gratuitous style cleanup]


Here is [something close to] your original code with the bugs annotated:

#include <stdio.h>
#include <stdlib.h>

// NOTE/BUG: this was not defined and _fixed_ defines should be all caps
#define maxSize     10

struct anArray {
    int numbers[maxSize];
    int swaps;
};

int counter;

void
initArray(struct anArray *array,const int *src)
{

    for (int idx = 0;  idx < maxSize;  ++idx)
        array->numbers[idx] = src[idx];

    array->swaps = 0;
}

void
swap(struct anArray *array, int first, int second)
{
    int temp = array->numbers[first];

    array->numbers[first] = array->numbers[second];
    array->numbers[second] = temp;
    counter++;
}

/* Partition function */
int
partition(struct anArray *array, int start, int end)
{
    if (start == end) {
        return start;
    }
    int pivot = array->numbers[end];
    int low = start - 1;
    int high = end;

    for (;;) {
        do {
            low++;
        } while (array->numbers[low] < pivot);

        do {
            high--;
        } while (array->numbers[high] > pivot);

        /* Detector for when the cells meet */
        if (low >= high) {
            swap(array, low, end);
            return low;
        }
    }
/* Swapping the values */
    swap(array, low, high);
}

void
quickSort(struct anArray *array, int start, int end)
{
    if (end - start <= 0) {
        return;
    }
    else {
        // NOTE/BUG: pivot is _not_ used
        int pivot = array->numbers[end];
        int partitionPoint = partition(array, start, end);

        quickSort(array, start, partitionPoint - 1);
        quickSort(array, partitionPoint + 1, end);
    }
}

int
main(void)
{
    // NOTE/BUG: we do _not_ want an array of the struct, but an array of int
    // that is allocated for "number" _inside_ the struct
    struct anArray *ascending = malloc(10 * sizeof(struct anArray));

    int ascend[maxSize] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };

    // NOTE/BUG: this was not defined
    initArray(ascending, ascend);

    quickSort(ascending, 0, maxSize - 1);
    ascending->swaps = counter;

    printf("Test: Unique random values\nSorted: [ ");
    for (int i = 0; i < maxSize; i++) {
        printf("%i ", ascending->numbers[i]);
    }
    printf("]\nSwaps: %i\nComps: \n\n", ascending->swaps);

    return 0;
}

Here is a cleaned up and working version. I've generalized it so it can take an arbitrarily long array. I've also done a bit of style and code cleanup:

#include <stdio.h>
#include <stdlib.h>

typedef struct {
    int *numbers;
    int size;
    int swaps;
} Array;

Array *
initArray(const int *src,int size)
{
    Array *array = malloc(sizeof(Array));

    array->numbers = malloc(size * sizeof(int));
    array->size = size;

    // store in reverse order so the sort will actually do something
    for (int idx = 0;  idx < size;  ++idx)
        array->numbers[size - 1 - idx] = src[idx];

    array->swaps = 0;

    return array;
}

void
freeArray(Array *array)
{

    free(array->numbers);
    free(array);
}

void
swap(Array *array, int first, int second)
{
    int temp = array->numbers[first];

    array->numbers[first] = array->numbers[second];
    array->numbers[second] = temp;

    array->swaps += 1;
}

/* Partition function */
int
partition(Array *array, int start, int end)
{

    if (start == end)
        return start;

    int pivot = array->numbers[end];
    int low = start - 1;
    int high = end;

    for (;;) {
        do {
            low++;
        } while (array->numbers[low] < pivot);

        do {
            high--;
        } while (array->numbers[high] > pivot);

        /* Detector for when the cells meet */
        if (low >= high) {
            swap(array, low, end);
            return low;
        }
    }

    /* Swapping the values */
    swap(array, low, high);
}

void
quickSort(Array *array, int start, int end)
{
    if (end - start <= 0)
        return;

    //int pivot = array->numbers[end];
    int partitionPoint = partition(array, start, end);

    quickSort(array, start, partitionPoint - 1);
    quickSort(array, partitionPoint + 1, end);
}

int
main(void)
{
    // NOTE/BUG: we do _not_ want an array of the struct, but an array of int
    // that is allocated for "number" _inside_ the struct

    int original[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
    int size = sizeof(original) / sizeof(original[0]);

    Array *ascending = initArray(original, size);

    quickSort(ascending, 0, ascending->size - 1);

    printf("Test: Unique random values\nSorted: [ ");
    for (int i = 0; i < ascending->size; i++) {
        int expected = original[i];
        int actual = ascending->numbers[i];
        printf("%d%s ", actual, (actual == expected) ? "" : "???");
    }
    printf("]\nSwaps: %i\nComps: \n\n", ascending->swaps);

    freeArray(ascending);

    return 0;
}

UPDATE:

What does the line int size = sizeof(original) / sizeof(original[0]); do exactly?

Does it give me an integer for size which I set to be the size of how many numbers I can hold in an array?

Yes, that is common/idiomatic trick to get the count of the number of elements of a fixed size array:

int array[] = { 1, 2, 3 };
size_t count = sizeof(array) / sizeof(array[0]);

Here, sizeof(array) is 3 times the size [in bytes] of the individual elements [which are int , which is 4 bytes], so we have 3 * 4 or 12.

sizeof(array[0]) is the size of the single, first element of the array, which is [again] an int , so this is 4.

So, when we divide the two, we have 12 / 4 or 3, which is the number of elements.

If so, wouldn't the amount of numbers I can hold be really small if sizeof(original[0]) happens to be very large?

No, because of the division. It doesn't care how large the element size [in bytes] is, because the ratio always produces the number of elements.

The sizeof(arr) / sizeof(arr[0]) trick is useful to get the count when we do: int arr[] = { ... };

If we do:

#define ARRCOUNT 3
int arr[ARRCOUNT] = { 1, 2, 3 };

We already know the count (ie it is ARRCOUNT ).

The [slight] advantage to the sizeof/sizeof trick is that if we had incorrectly defined ARRCOUNT as 4 by mistake, it would still compile, link, and run, but would produce incorrect results [because there were only 3 elements].

This is a common enough trick that we can define a generic macro [that we can reuse by putting it a .h file]:

#define ARRAY_COUNT(arr_) (sizeof(arr_) / sizeof(arr_))

UPDATE #2:

I've tried your code (even tried copying and pasting it) but my swaps is still showing 9 despite my array to be sorted is just going from { 1 to 10}. Not sure why this keeps occurring.

I believe [now] you have a bug in the sort itself.

I've produced another version that has much more extensive test data generation and comparison.

At a minimum, because of the way the tests are structured, the first element of the sorted array should always have a value of 1.

The test that fails is the one that does a random shuffle of the original array before sending it in to be sorted.

You can add other tests as needed. The array needn't be so large to show the problem. For example, the following single test is enough to produce the error:

bigtest(100,237,1);

Anyway, here is the enhanced diagnostic code:

#include <stdio.h>
#include <stdlib.h>

#define MAXLEN      60

typedef struct {
    int *numbers;
    int size;
    int swaps;
} Array;

Array *
initArray(const int *src,int size,int randshuf)
{
    int idx;
    Array *array = malloc(sizeof(Array));

    array->numbers = malloc(size * sizeof(int));
    array->size = size;
    array->swaps = 0;

    // store in reverse order so the sort will actually do something
    switch (randshuf) {
    case 0:  // reverse the numbers
        for (idx = 0;  idx < size;  ++idx)
            array->numbers[size - 1 - idx] = src[idx];
        break;

    default:  // do _crude_ random shuffle
        for (idx = 0;  idx < size;  ++idx)
            array->numbers[idx] = 0;

        for (idx = 0;  idx < size;  ++idx) {
            while (1) {
                int ridx = rand() % size;
                if (array->numbers[ridx] == 0) {
                    array->numbers[ridx] = src[idx];
                    break;
                }
            }
        }
        break;
    }

    return array;
}

void
freeArray(Array *array)
{

    free(array->numbers);
    free(array);
}

void
swap(Array *array, int first, int second)
{
    int temp = array->numbers[first];

    array->numbers[first] = array->numbers[second];
    array->numbers[second] = temp;

    array->swaps += 1;
}

/* Partition function */
int
partition(Array *array, int start, int end)
{

    if (start == end)
        return start;

    int pivot = array->numbers[end];
    int low = start - 1;
    int high = end;

    for (;;) {
        do {
            low++;
        } while (array->numbers[low] < pivot);

        do {
            high--;
        } while (array->numbers[high] > pivot);

        /* Detector for when the cells meet */
        if (low >= high) {
            swap(array, low, end);
            return low;
        }
    }

    /* Swapping the values */
    swap(array, low, high);
}

void
quickSort(Array *array, int start, int end)
{
    if (end - start <= 0)
        return;

    //int pivot = array->numbers[end];
    int partitionPoint = partition(array, start, end);

    quickSort(array, start, partitionPoint - 1);
    quickSort(array, partitionPoint + 1, end);
}

void
print_orig(const int *orig,int count)
{
    int len = 0;

    printf("Test: Original numbers (%d):\n",count);

    for (int idx = 0;  idx < count;  ++idx) {
        len += printf(" %10d ", orig[idx]);
        if (len >= MAXLEN) {
            printf("\n");
            len = 0;
        }
    }

    if (len > 0)
        printf("\n");
}

int
print_array(Array *array,const int *orig,const char *reason)
{
    int len = 0;
    int cmp;
    int err = -1;

    printf("Test: Array Values (%s):\n",reason);

    for (int idx = 0; idx < array->size; ++idx) {
        int actual = array->numbers[idx];

        if (orig != NULL) {
            int expected = orig[idx];
            cmp = (actual == expected);
        }
        else
            cmp = 1;

        len += printf(" %10d%c", actual, cmp ? ' ' : '?');

        if (len >= MAXLEN) {
            printf("\n");
            len = 0;
        }

        if (cmp)
            continue;
        if (err < 0)
            err = idx;
    }

    if (orig != NULL)
        printf("\nSwaps: %i\nComps: \n\n", array->swaps);
    else {
        if (len > 0)
            printf("\n");
    }

    return err;
}

void
bigtest(int count,int randgap,int randshuf)
// count -- number of elements (negative means random)
// randgap -- gap between element values (negative means random)
// randshuf -- 0=simple reverse, 1=random shuffle
{
    int *orig;
    Array *array;

    printf("\n");
    for (int idx = 1;  idx <= 80;  ++idx)
        printf("-");
    printf("\n");

    printf("COUNT: %d, RANDGAP: %d, RANDSHUF: %d\n",count,randgap,randshuf);

    // get number of elements
    if (count < 0)
        count = (rand() % count) + 1;

    // get element gap (e.g. 1 --> {1, 2, 3}, 2 --> { 1, 3, 5 }
    if (randgap < 0)
        randgap = (rand() % randgap) + 1;

    printf("COUNT: %d, RANDGAP: %d, RANDSHUF: %d\n",count,randgap,randshuf);

    // get original array
    orig = malloc(sizeof(int) * count);

    // fill in original array
    do {
        int val = 1;

        // simple gap
        if (randgap >= 0) {
            if (randgap == 0)
                randgap = 1;
            for (int idx = 0;  idx < count;  ++idx, val += randgap)
                orig[idx] = val;
            break;
        }

        // random gap
        int gap;
        for (int idx = 0;  idx < count;  ++idx, val += gap) {
            orig[idx] = val;
            gap = (rand() % randgap) + 1;
        }
    } while (0);

    print_orig(orig,count);

    array = initArray(orig,count,randshuf);
    print_array(array,NULL,"Shuffled");

    quickSort(array, 0, array->size - 1);

    print_array(array,orig,"Sorted");

    freeArray(array);
    free(orig);
}

int
main(void)
{

    bigtest(10,0,0);
    bigtest(-100,23,0);
    bigtest(-1000,-2337,0);
    bigtest(-1000,-2337,1);

    return 0;
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM