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 在下面的 cmets 中所指出的,上面的代码并不是真正的时间效率,因为合并部分是 O(N²)。使用仅包含要以特定方式排序的元素的临时数组,以下程序只需要 O(N) 额外时间和 O(N/K) 空间,其中 K 是所需的不同排序顺序的数量(2 in OP的问题)。
Jonathan Leffler 还指出,它可以变得更通用,允许算法“处理 3、4、……N 个均匀交错的子数组,每个子数组可能具有不同的排序顺序”。我在下面的 sn-p 中通过将一个指针数组传递给排序函数来实现它,以比较函数。
#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("");
}