/ *我最近学习了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;
}
答案 0 :(得分:2)
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
$
一些补充说明:
我(和提问者)使用qsort()
的方式,它一次处理两个连续的int
值。因此,必须授予该数组具有适当数量的元素。 (否则,qsort()
要么进行越界访问,要么不能考虑最后一个元素。)为了考虑这个事实,我插入了
assert(!(size & 1));
可以读作&#34;确保数组具有偶数个元素。&#34;
我决定将单独的函数compEven()
和compOdd()
作为恕我直言,简化了一些事情。我改变了两者的签名以满足我的需求,并从gcc中得到关于错误功能签名的抱怨(警告)。因此,我将函数指针转换为期望的类型(使gcc无声)。
Jonathon提供了一个很好的提示,使比较函数可以抵御下溢问题。当差异大于return p1[0] - p2[0];
或小于INT_MAX
时,INT_MIN
会导致错误的结果。相反,他建议使用:
return (p1[0] > p2[0]) - (p1[0] < p2[0]);
永远不会出现任何上溢/下溢问题。
工作原理:
如果a < b
:(a > b) - (a < b)
⇒0 - 1
⇒-1
如果a == b
:(a > b) - (a < b)
⇒0 - 0
⇒0
如果a > b
:(a > b) - (a < b)
⇒1 - 0
⇒1
非常聪明的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("");
}