使用qsort函数以交替方式对整数数组进行排序。

时间:2017-12-02 11:38:12

标签: c algorithm sorting

/ *我最近学习了qsort功能。这个c代码给出了错误的输出。需要帮助。 问题 - 以交替方式对整数数组进行排序。 (偶数指数的元素和奇数指数的元素分别排序) 输出 - 0 4 1 2 5 8 7 5 9 3         10 5 * /

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

// This function is used in qsort to decide the relative order
// of elements at addresses p and q.
int comparator(const void *p, const void *q)
{
    // Get the values at given addresses
    int l = *(const int *)p;
    int r = *(const int *)q;

    return (l-r);
}

// A utility function to print an array
void printArr(int arr[], int n)
{
    int i;
    for (i = 0; i < n; i = i+1)
        printf("%d ", arr[i]);
}

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

    int size0 = sizeof(arr) / sizeof(arr[0]);
    int size1 = (int) ((float)sizeof(arr) / sizeof(arr[0]) / 2 + 0.5);
    int size2 = size0 - size1;

    qsort((void *)arr+1, size2, 2*sizeof(arr[0]), comparator);   
    //sort odd positions

    qsort((void *)arr, size1, 2*sizeof(arr[0]), comparator);
    //sort even positions

    printf("Output array is\n");
    printArr(arr, size0);
    printf("\n%d %d", size0, size1);

    return 0;
}

3 个答案:

答案 0 :(得分:2)

qsort

  

void qsort(void * ptr,size_t count,size_t size,               int(* comp)(const void *,const void *));

     

按升序对ptr指向的给定数组进行排序。该数组包含 size 字节的计数元素。 comp指向的函数用于对象比较。

     

ptr - 指向要排序的数组的指针

     

count - 数组中元素的数量

     

size - 数组中每个元素的大小(以字节为单位)

     

comp - 比较函数,如果返回负整数值   第一个参数小于第二个参数,

在您的程序中,您将每个元素的大小传递为2*sizeof(arr[0]),这导致8个字节,这是对qsort()的错误输入。因此,您的输出结果不正确。

答案 1 :(得分:1)

可以使用qsort()分别对偶数/奇数元素进行排序。 但是,必须稍微更改设置才能完成此操作。

正如彼得提到的那样(我之前没有考虑过,因为我必须承认)对偶数元素进行排序将会摧毁&#34;奇数元素作为交换的结果考虑了元素大小,表示为偶数奇数元素对。

考虑到这一点,如果在第二次排序完成之前保存了第一次排序的结果,则可以为所有这些做出解决方案。

在我的示例中,我在第一次排序后复制了相关元素,并在第二次排序后合并它们。

这是我的样本testQSortEvenOdd.c

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

int compEven(const int *p1, const int *p2)
{
  return (p1[0] > p2[0]) - (p1[0] < p2[0]);
}

int compOdd(const int *p1, const int *p2)
{
  return (p1[1] > p2[1]) - (p1[1] < p2[1]);
}

void printArray(size_t n, int *arr, int step)
{
  for (; n--; arr += step) printf(" %d", *arr);
  putchar('\n');
}

int main()
{
  int arr[] = { 1, 4, 7, 2, 9, 3, 0, 8, 6, 5 };
  enum { size = sizeof arr / sizeof *arr };
  assert(!(size & 1));
  /* sort odd positions */
  qsort(arr, (size + 1) / 2, 2 * sizeof *arr,
    (int(*)(const void*, const void*))&compOdd);
  /* output of sorted array for odd positions */
  puts("Odd elements sorted:");
  printArray(size / 2, arr + 1, 2);
  int arrRes[(size + 1) / 2];
  for (size_t i = 1; i < size; i += 2) arrRes[i / 2] = arr[i];
  /* sort even positions */
  qsort(arr, (size + 1) / 2, 2 * sizeof *arr,
    (int(*)(const void*, const void*))&compEven);
  /* output of sorted array for even positions */
  puts("Even elements sorted:");
  printArray((size + 1) / 2, arr, 2);
  /* merge array with copy */
  for (size_t i = 1; i < size; i += 2) arr[i] = arrRes[i / 2];
  puts("Merged elements:");
  printArray(size, arr, 1);
  /* done */
  return 0;
}

在Windows 10(64位)上以Cygwin进行测试:

$ gcc --version
gcc (GCC) 6.4.0

$ gcc -std=c11 -o testQSortEvenOdd testQSortEvenOdd.c 

$ ./testQSortEvenOdd
Odd elements sorted:
 2 3 4 5 8
Even elements sorted:
 0 1 6 7 9
Merged elements:
 0 2 1 3 6 4 7 5 9 8

$

一些补充说明:

  1. 我(和提问者)使用qsort()的方式,它一次处理两个连续的int值。因此,必须授予该数组具有适当数量的元素。 (否则,qsort()要么进行越界访问,要么不能考虑最后一个元素。)为了考虑这个事实,我插入了

    assert(!(size & 1));

    可以读作&#34;确保数组具有偶数个元素。&#34;

  2. 我决定将单独的函数compEven()compOdd()作为恕我直言,简化了一些事情。我改变了两者的签名以满足我的需求,并从gcc中得到关于错误功能签名的抱怨(警告)。因此,我将函数指针转换为期望的类型(使gcc无声)。

  3. Jonathon提供了一个很好的提示,使比较函数可以抵御下溢问题。当差异大于return p1[0] - p2[0];或小于INT_MAX时,INT_MIN会导致错误的结果。相反,他建议使用:

    return (p1[0] > p2[0]) - (p1[0] < p2[0]);

    永远不会出现任何上溢/下溢问题。

  4. 工作原理:

    如果a < b(a > b) - (a < b)0 - 1-1

    如果a == b(a > b) - (a < b)0 - 00

    如果a > b(a > b) - (a < b)1 - 01

    非常聪明的Jonathan Leffler - 我印象深刻。

答案 2 :(得分:1)

qsort需要一个连续的内存块才能正常运行。

如果你需要分别对奇数和偶数索引元素进行排序,你可以先将元素分开,然后对它们进行独立排序,然后合并这两个部分。

即使不分配任何额外的内存,你也可以这样做:

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

int less_int(const void *lhs, const void *rhs)
{
    return *(const int *)lhs < *(const int *)rhs ? -1
        : *(const int *)lhs > *(const int *)rhs ? 1 : 0;
}

int greater_int(const void *lhs, const void *rhs)
{
    return *(const int *)lhs > *(const int *)rhs ? -1
        : *(const int *)lhs < *(const int *)rhs ? 1 : 0;
}

void sort_ascending(int* arr, size_t n)
{
    qsort(arr, n, sizeof *arr, less_int);
}

void sort_descending(int* arr, size_t n)
{
    qsort(arr, n, sizeof *arr, greater_int);
}

inline void swap_int(int* a, int* b)
{
    int tmp = *a;
    *a = *b;
    *b = tmp;
}

size_t partition_odd_even(int* arr, size_t n )
{
    size_t n_odds = n - n / 2;
    for (size_t i = 1, j = n_odds + n_odds % 2; i < n_odds; i += 2, j += 2)
    {
        swap_int(arr + i, arr + j);
    }
    return n_odds;
}

void interleave_odd_even(int* arr, size_t n )
{
    size_t n_odds = n - n / 2;
    for (size_t i = 1; i < n_odds; ++i )
    {
        for (size_t j = n_odds - i; j < n_odds + i; j += 2)
        {
            swap_int(arr + j, arr + j + 1);
        }
    }
}

void print_arr(int* arr, size_t n);

int main(void)
{
    int arr[] = {1, 4, 7, 2, 9, 3, 0, 8, 6, 5};
    size_t arr_size = sizeof arr / sizeof *arr;
    print_arr(arr, arr_size);

    size_t n_odds = partition_odd_even(arr, arr_size);
    size_t n_evens = arr_size - n_odds;
//  print_arr(arr, arr_size);

    sort_ascending(arr, n_odds);
//  print_arr(arr, n_odds);

    sort_descending(arr + n_odds, n_evens);
//  print_arr(arr + n_odds, n_evens);

    interleave_odd_even(arr, arr_size);
    print_arr(arr, arr_size);

    return 0;
}

void print_arr(int* arr, size_t n)
{
    for(size_t i = 0; i < n; ++i)
    {
        printf(" %d", arr[i]);  
    }
    puts("");
}

给出了:

 1 4 7 2 9 3 0 8 6 5
 0 8 1 5 6 4 7 3 9 2

修改

如下面greybeard的评论中所述,上面的代码实际上并不具有时间效率,因为合并部分是O(N²)。使用仅包含要以特定方式排序的元素的临时数组,以下程序仅需要O(N)额外时间和O(N / K)空间,其中K是所需的不同排序顺序的数量(2 in OP的问题)。

Jonathan Leffler还指出,可以更加通用,允许算法“处理3,4,... N均匀隔行扫描的子阵列,可能每个”。我通过向sort函数传递一个指向比较函数的指针数组,在下面的代码片段中实现了它。

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

// compare functions
typedef int (*PCMPFN)(const void*, const void*);

int ascending_cmp_int(const void *lhs, const void *rhs)
{
    return *(const int *)lhs < *(const int *)rhs ? -1
        : *(const int *)lhs > *(const int *)rhs ? 1 : 0;
}

int descending_cmp_int(const void *lhs, const void *rhs)
{
    return *(const int *)lhs > *(const int *)rhs ? -1
        : *(const int *)lhs < *(const int *)rhs ? 1 : 0;
}

// This function is never called. Whithout knowing the actual implementation
// of 'qsort' we can't make any assumption
int untouched_cmp_int(const void *lhs, const void *rhs)
{
    (void)lhs;  // Those parameters are unused here, this is to avoid a warning
    (void)rhs; 
    return 0;
}

// Copy the elements of the source array starting from index 'start' with stride 'step'
size_t strided_split(int* dest, const int *src, size_t n, size_t start, size_t step)
{
    size_t j = 0;
    for (size_t i = start; i < n; i += step, ++j)
    {
        dest[j] = src[i];
    }
    return j;
}

// Inverse of the previous
void strided_merge(int* dest, const int *src, size_t n, size_t start, size_t step)
{
    for (size_t i = start, j = 0; j < n; i += step, ++j)
    {
        dest[i] = src[j];
    }
}

// Apply different sort orders to different elements
void alternate_sort(int* arr, const size_t n, PCMPFN comps[], const size_t k)
{
    int tmp[n/k + 1];         // Please note that VLA are optional in C11

    for ( size_t i = 0; i < k; ++i )
    {
        if ( comps[i] == untouched_cmp_int )
            continue;
        // First select the elements 
        size_t n_copied = strided_split(tmp, arr, n, i, k);
        // then sort only them as needed
        qsort(tmp, n_copied, sizeof tmp[0], comps[i]);
        // Once sorted, copy back the elements in the source array
        strided_merge(arr, tmp, n_copied, i, k);
    }
}

void print_arr(const int* arr, const size_t n);

int main(void)
{
    int arr[] = {1, 4, 7, 2, 9, 3, 0, 8, 6, 5};
    const size_t N = sizeof arr / sizeof *arr;
    print_arr(arr, N);

    PCMPFN compares[] = {
        descending_cmp_int, untouched_cmp_int, ascending_cmp_int
    };
    const size_t K = sizeof compares / sizeof *compares;

    alternate_sort(arr, N, compares, K);
    print_arr(arr, N);

    return 0;
}

void print_arr(const int* arr, const size_t n)
{
    for(size_t i = 0; i < n; ++i)
    {
        printf(" %d", arr[i]);  
    }
    puts("");
}