简体   繁体   中英

Sort only either odd or even numbers from an array

I need to sort either the odd or even numbers in an array while leaving the rest unchanged. As this is part of a memory management course, I would strongly prefer not to duplicate any data so I didn't go for the obvious solution.

I thought about using a simple bubble sort function by skipping the "bad" first or second params of the comparison. I will post the function only as the rest is irrelevant.

//these are passed as sort_type_remainder
#define REMAINDER_EVEN 0
#define REMAINDER_ODD 1

void sort_integers(int *integer_list, int num_integers, int sort_type_remainder)
{
    int i, j, aux, offset = 1;
    for(i = 0; i < num_integers - 1; i++)
    {
        offset = 1;
        for(j = 0; j + offset < num_integers - i; j++)
        {
            if(abs(integer_list[j] % 2) != sort_type_remainder) //first element to be compared is of wrong type, skip
            {
                j++;
            }
            else
            {
                //first element is of good type, but second element is not, increase offset and reloop first element
                if(abs(integer_list[j + offset] % 2) != sort_type_remainder)
                {
                    j--;
                    offset++;
                }
                else //both elements are of good type, bubble sort them
                {
                    if(integer_list[j] > integer_list[j + offset])
                    {
                        aux = integer_list[j + offset];
                        integer_list[j + offset] = integer_list[j];
                        integer_list[j] = aux;
                    }
                    offset = 1;
                }
            }
        }
    }
}

This kind of works but some inputs are not processed properly and I can not figure out why. I tried reducing the input size to get a simpler way to reproduce this but as soon as I do so it starts working properly.

Here is the input / output, I deleted the even numbers as they stay in their position and it makes the error easier to notice.

odd
20
13 5 -3 4 2 -7 23 -33 1 6 -8 7 10 -51 9 -5 12 92 69 55

产量

Any help or suggestions would be greatly appreciated, preferably with an explanation not just a solution.

Edit:: full program is here, don't want to clutter the question http://pastebin.com/vZDcmppV

What's wanted

Given the description and the input data:

  13   5  -3   4   2  -7  23 -33   1   6  -8   7  10 -51   9  -5  12  92  69  55

I believe that the requirement to sort the odd numbers into ascending order leaving the even numbers unmoved requires the output:

 -51 -33  -7   4   2  -5  -3   1   5   6  -8   7  10   9  13  23  12  92  55  69

And to sort the even numbers into ascending order leaving the odd numbers unmoved requires the output:

  13   5  -3  -8   2  -7  23 -33   1   4   6   7  10 -51   9  -5  12  92  69  55

Concept behind the algorithm

One way to do this is to create an array of indices of the positions to be sorted:

int O = 13; // for sorting odd:
int opos[] = { 0,  1,  2,  5,  6,  7,  8, 11, 13, 14, 15, 18, 19 };

int E =  7; // for sorting even:
int epos[] = {  3,  4,  9, 10, 12, 16, 17 };

Then any sorting algorithm that would normally scan for (i = 0; i < N; i++) and then references A[i] would instead scan for (e = 0; e < E; e++) and reference A[epos[e]] .

So, we start with a simple-minded bubble sort that sorts a complete array A of type int with N elements:

void bsort_1(int *A, int N)
{
     for (int i = 0; i < N - 1; i++)
     {
         for (int j = 0; j < N - 1 - i; j++)
         {
              if (A[j+1] < A[j])
              {
                  int t = A[j+1];
                  A[j+1] = A[j];
                  A[j] = t;
              }
         }
    }
}

Using O(N) auxilliary storage

We can adapt that to sort only the elements with a specified parity (0 for even, 1 for odd):

void bsort_4(int *A, int N, int parity)
{
    assert(parity == 0 || parity == 1);
    assert(N < 1024 * 1024);
    int posn[N];
    int count = 0;
    for (int i = 0; i < N; i++)
    {
        if ((A[i] & 1) == parity)
            posn[count++] = i;
    }
    if (count < 2)
        return;
    for (int i = 0; i < count - 1; i++)
    {
        for (int j = 0; j < count - 1 - i; j++)
        {
            if (A[posn[j + 1]] < A[posn[j]])
            {
                int t = A[posn[j + 1]];
                A[posn[j + 1]] = A[posn[j]];
                A[posn[j]] = t;
            }
        }
    }
}

This establishes the indexes that correspond to odd or even entries in the array, and then sorts just those elements of the array.

Test code and results

That needs some test code to be reasonably confident it is doing what it is supposed to do.

#include <assert.h>
#include <stdio.h>

static void bsort_4(int *A, int N, int parity)
{
    assert(parity == 0 || parity == 1);
    assert(N < 1024 * 1024);
    int posn[N];
    int count = 0;
    for (int i = 0; i < N; i++)
    {
        if ((A[i] & 1) == parity)
            posn[count++] = i;
    }
    if (count < 2)
        return;
    for (int i = 0; i < count - 1; i++)
    {
        for (int j = 0; j < count - 1 - i; j++)
        {
            if (A[posn[j + 1]] < A[posn[j]])
            {
                int t = A[posn[j + 1]];
                A[posn[j + 1]] = A[posn[j]];
                A[posn[j]] = t;
            }
        }
    }
}

static void dump_array(const char *tag, const int *a, int n)
{
    printf("%-8s", tag);
    for (int i = 0; i < n; i++)
        printf(" %3d", a[i]);
    putchar('\n');
}

static void test_sorting(const int *data, int size, int parity)
{
    int array[size];
    for (int i = 0; i < size; i++)
        array[i] = data[i];
    dump_array("Before:", array, size);
    bsort_4(array, size, parity);
    dump_array("After:", array, size);
}

int main(void)
{
    const int array[] =
    {
        13,   5,  -3,   4,   2,  -7,  23, -33,   1,   6,
        -8,   7,  10, -51,   9,  -5,  12,  92,  69,  55,
    };
    enum { A_SIZE = sizeof(array) / sizeof(array[0]) };

    printf("Sort even numbers:\n");
    test_sorting(array, A_SIZE, 0);
    printf("Sort odd  numbers:\n");
    test_sorting(array, A_SIZE, 1);

    return 0;
}

Output:

Sort even numbers:
Before:   13   5  -3   4   2  -7  23 -33   1   6  -8   7  10 -51   9  -5  12  92  69  55
After:    13   5  -3  -8   2  -7  23 -33   1   4   6   7  10 -51   9  -5  12  92  69  55
Sort odd  numbers:
Before:   13   5  -3   4   2  -7  23 -33   1   6  -8   7  10 -51   9  -5  12  92  69  55
After:   -51 -33  -7   4   2  -5  -3   1   5   6  -8   7  10   9  13  23  12  92  55  69

You have to take my word for it that the required outputs were created before running any of the programs that I used to arrive at this final result.

This code can be adapted to other sort strategies — insertion, selection, shell, merge, quick. The scan that sets up the posn array is needed in the top-level operation (but should be avoided for recursive calls in sorts like merge sort and quick sort), and then the rest of the code uses the extra level of indirection.

Avoiding O(N) auxilliary storage

If the extra array used isn't acceptable for some reason, then you have to go about things in a much more contorted way, and you get a larger runtime penalty because you have to calculate the elements of the posn array on the fly, rather than precomputing them once. The comparisons in the sort are always between adjacent positions. Without the posn array, the code still establishes the count of odd or even values, but there's be code to step through the data array to find the next value with the correct parity:

#include <assert.h>
#include <stdio.h>

static void bsort_4(int *A, int N, int parity)
{
    assert(parity == 0 || parity == 1);
    assert(N < 1024 * 1024);
    int count = 0;
    for (int i = 0; i < N; i++)
    {
        if ((A[i] & 1) == parity)
            count++;
    }
    if (count < 2)
        return;
    for (int i = 0; i < count - 1; i++)
    {
        int i0 = 0;
        while ((A[i0] & 1) != parity)
            i0++;
        for (int i1 = 0; i1 < i; i1++)
        {
            while ((A[i0] & 1) != parity)
                i0++;
        }
        int j0 = i0;
        for (int j = 0; j < count - 1 - i; j++)
        {
            int j1 = j0 + 1;
            while ((A[j1] & 1) != parity)
                j1++;
            /*printf("P = %d; j0 = %2d; A[j0] = %3d; j1 = %2d; A[j1] = %3d\n",*/
            /*       parity, j0, A[j0], j1, A[j1]);*/
            if (A[j1] < A[j0])
            {
                int t = A[j1];
                A[j1] = A[j0];
                A[j0] = t;
            }
            j0 = j1;
        }
    }
}

static void dump_array(const char *tag, const int *a, int n)
{
    printf("%-8s", tag);
    for (int i = 0; i < n; i++)
        printf(" %3d", a[i]);
    putchar('\n');
}

static void test_sorting(const int *data, int size, int parity)
{
    int array[size];
    for (int i = 0; i < size; i++)
        array[i] = data[i];
    dump_array("Before:", array, size);
    bsort_4(array, size, parity);
    dump_array("After:", array, size);
}

int main(void)
{
    const int array[] =
    {
        13,   5,  -3,   4,   2,  -7,  23, -33,   1,   6,
        -8,   7,  10, -51,   9,  -5,  12,  92,  69,  55,
    };
    enum { A_SIZE = sizeof(array) / sizeof(array[0]) };

    printf("Sort even numbers:\n");
    test_sorting(array, A_SIZE, 0);
    printf("Sort odd  numbers:\n");
    test_sorting(array, A_SIZE, 1);

    return 0;
}

I'm not happy about the variable naming in bsort_5() , but I don't immediately have better names to use.

Output:

Sort even numbers:
Before:   13   5  -3   4   2  -7  23 -33   1   6  -8   7  10 -51   9  -5  12  92  69  55
After:    13   5  -3  -8   2  -7  23 -33   1   4   6   7  10 -51   9  -5  12  92  69  55
Sort odd  numbers:
Before:   13   5  -3   4   2  -7  23 -33   1   6  -8   7  10 -51   9  -5  12  92  69  55
After:   -51 -33  -7   4   2  -5  -3   1   5   6  -8   7  10   9  13  23  12  92  55  69

Note that this relies on the fact that the bubble sort shown only compares adjacent entries of the same parity. For sorts like shell, merge, quick sort which work on arbitrary positions, it is not appropriate — the loops to find the right indices would be appallingly expensive. You'd almost certainly be better off with the bubble sort (or maybe selection sort or insertion sort).

GitHub

This code is available in my SOQ (Stack Overflow Questions) repository on GitHub as files bsort1.c , bsort4.c and bsort5.c in the src/so-4295-4541 sub-directory.

If you need to sort either odd or even numbers from an array, this is my method:

I would first of all pick from the starting array all odd or even numbers depending and what you want to do, replace everything by an indicator (1 or 2 depends if you want to sort odds or evens).

Then sort the array by itself (if you want to use another algorithm than the bubble sort, there's Big-o cheat sheet , very useful).

And then you just scan your first array by replacing every indice by every sorted numbers depending on their position.

In short :

You want to put every odd (or even) number from array 1 into array 2, and replace them by an indicator (anything you might recognize when scaning array 1 after).

You want to sort array 2.

Then you want to replace every indicator by every element from array 2.

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