0

I am trying to sort an array of integers so that the evens print out in descending order FIRST and then the odd numbers in the array print out in ascending order.

So the output would look like: 
8 6 4 2 1 3 5 7 9

How would I go about doing this?

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

int compare(const void *p, const void *q);
void printArr(int arr[], int n);

//Driver program to test sort
int main()
{
    int nums[] = {1, 2, 3, 4, 5, 6, 7, 8, 9};

    int size = sizeof(nums) / sizeof(nums[0]);
    qsort((void*) nums, size, sizeof(nums[0]), compare);

    printf("Sorted array is\n");
    printArr(nums, size);

    return 0;
}

//This function is used in qsort to decide the relative order of elements at addresses p and q
int compare(const void *p, const void *q)
{
    return ( *(int*)p < *(int*)q);

}
//A utility function to print an array
void printArr(int arr[], int n)
{
    int i;
    for (i = 0; i < n; ++i)
        printf("%d ", arr[i]);
}
R. Lee
  • 63
  • 5
  • 4
    It's all about the comparison function. Hint: an even number is always less than an odd number. – user3386109 Mar 16 '18 at 04:34
  • 1
    OP's `compare()` does not work well with `qsort()`. `qsort()` expects a `-`, `+` or 0 result, not 0,1. – chux - Reinstate Monica Mar 16 '18 at 04:37
  • You could also do one thing divide the initial array into two temporary arrays one for sorted even numbers and another one for sorted odd numbers. then combine those two arrays into one and print them in the way you want. – Aniruddh Agarwal Mar 16 '18 at 05:02
  • You could search for previous questions, using perhaps '`[c] sort ascending descending odd even`' as a search term. The duplicate sorts odd and even slightly differently, but the basic idea is exactly the same — treat odd and even values differently, and sort one set before the other, and possibly in different directions. It's all pretty straight-forward once you've got the basic idea, which the answers below also outline quite well. – Jonathan Leffler Mar 16 '18 at 05:05
  • @JonathanLeffler, your duplicate is C++ (and one very awkward C answer) Granted principals are the same, but implementations differ. – David C. Rankin Mar 16 '18 at 05:23
  • @DavidC.Rankin — OK; I'll find others...there are a number of possibilities. – Jonathan Leffler Mar 16 '18 at 05:28
  • OK, thanks. I figured there were, I just couldn't put my finger on one. – David C. Rankin Mar 16 '18 at 05:34
  • @DavidC.Rankin: I didn't find an especially better Q&A, so I added an answer to it from my [SOQ](https://github.com/jleffler/soq) (Stack Overflow Questions) repository on GitHub, extending the file `oddascevendesc.c` in the [doc/qsort](https://github.com/jleffler/soq/tree/master/doc/qsort) sub-directory to cover more of the cases in pure C. I already had two of the four contrariwise orders in place and added the other two (a few minutes work); I haven't added the 'odd before even' or 'even before odd' all in one direction alternatives, but they're easily created from those that are present. – Jonathan Leffler Mar 16 '18 at 06:34
  • Above and beyond. That insures a good duplicate in addition to what we have here. – David C. Rankin Mar 16 '18 at 06:57

2 Answers2

2

As it is your comparison function does not match what qsort is looking for. The rules are as follows (assuming the first input is p, second input is q):

  • -1 means that p should come before q
  • 0 means that they are equal
  • 1 means that q should come before p

That being said, the key that will allow you make this work is to change your comparison function to deal with even/odd numbers differently. More specifically, you need to check if the first is even and the second is odd, first is odd second is even, both even, or both odd, and act accordingly.

Here is the setup (keep in mind that modulus is expensive, so this isn't the most efficient):

int first = *(int *)p;
int second = *(int *)q;
int firstIsOdd = first % 2; // Returns 1 if odd, 0 if even
int secondIsOdd = second % 2;

Now that you know which one is even and which one is odd you can deal with each case differently. Here are the rules:

  1. First is odd and second is even? Return 1 so that second comes before first in the list.
  2. First is even and second is odd? Return -1 so that first comes before second in the list.
  3. Both odd? Compare them normally to achieve ascending order (i.e., return 0 if equal, -1 if first is less than second, 1 if second is less than first)
  4. Both even? Compare them, but do the opposite of what you would normally do to achieve descending order (i.e., return 0 if equal, -1 if second is less than first, 1 if first is less than second)
Stephano
  • 356
  • 1
  • 5
1

In the compare function, test whether p and q are even numbers. There are four possible combinations of even and odd numbers {ee, eo, oe, oo}. For example, if they are both even, compare them one way e.g. return p>q the ((int*)p < *(int*)q) that you have plus, in this case, false needs to be negative. If p is even then p is lesser, if p is odd and q is even, then p is greater. If they are both odd compare them the other way e.g. p<q.

mbowden
  • 687
  • 6
  • 7