您可以使用标准的qsort() 函数以及适当的比较函数。因为你也需要N,所以它必须是一个全局变量。下面是一个这样的比较器(带有测试工具)——但请注意第三个程序中更简单的比较器(总是有改进的余地):
/*
** Sort values greater than N first in ascending order, and values less
** than N last in descending order, with values equal to N in the middle.
*/
#include <stdio.h>
#include <stdlib.h>
static int N;
static int comparator(const void *v1, const void *v2)
{
int a = *(int *)v1;
int b = *(int *)v2;
if (a < N && b < N)
return (a < b) - (a > b); /* Descending */
else if (a > N && b > N)
return (a > b) - (a < b); /* Ascending */
else if (a > N && b < N)
return -1;
else if (a < N && b > N)
return +1;
else if (a == N && b > N)
return +1;
else if (b == N && a < N)
return +1;
else if (a == N && b < N)
return -1;
else if (b == N && a > N)
return -1;
else
return 0;
}
static void dump_array(const char *tag, size_t n, int *data)
{
printf("%s (%zu):\n", tag, n);
const char *pad = "";
size_t i;
for (i = 0; i < n; i++)
{
if (i > 0 && i % 10 == 0)
{
putchar('\n');
pad = "";
}
printf("%s%3d", pad, data[i]);
pad = ",";
}
if (i % 10 != 0)
putchar('\n');
putchar('\n');
}
int main(void)
{
// random -n 100 0 99 | commalist -B 8 -n 10 -W 2
static int data[] =
{
22, 90, 87, 54, 81, 72, 68, 44, 82, 56,
45, 66, 97, 69, 84, 7, 47, 27, 77, 11,
99, 57, 95, 61, 90, 10, 98, 17, 29, 26,
5, 39, 8, 61, 38, 90, 92, 85, 19, 39,
76, 34, 97, 7, 23, 19, 27, 71, 8, 59,
64, 25, 78, 28, 6, 65, 32, 47, 96, 3,
55, 9, 75, 59, 5, 71, 83, 0, 95, 2,
38, 61, 96, 94, 75, 40, 87, 75, 58, 49,
4, 48, 58, 8, 99, 60, 91, 91, 46, 27,
90, 85, 53, 60, 85, 46, 51, 33, 71, 92,
13, 14, 60, 3, 94, 38, 2, 62, 33, 27,
69, 33, 36, 66, 40, 63, 53, 34, 11, 4,
56, 66, 86, 5, 95, 84, 69, 49, 49, 39,
48, 91, 22, 35, 50, 64, 94, 35, 44, 97,
70, 14, 28, 36, 68, 52, 69, 2, 54, 80,
};
enum { NUM_DATA = sizeof(data) / sizeof(data[0]) };
N = 56;
dump_array("Before", NUM_DATA, data);
qsort(data, NUM_DATA, sizeof(data[0]), comparator);
dump_array("After", NUM_DATA, data);
return 0;
}
输出:
Before (150):
22, 90, 87, 54, 81, 72, 68, 44, 82, 56
45, 66, 97, 69, 84, 7, 47, 27, 77, 11
99, 57, 95, 61, 90, 10, 98, 17, 29, 26
5, 39, 8, 61, 38, 90, 92, 85, 19, 39
76, 34, 97, 7, 23, 19, 27, 71, 8, 59
64, 25, 78, 28, 6, 65, 32, 47, 96, 3
55, 9, 75, 59, 5, 71, 83, 0, 95, 2
38, 61, 96, 94, 75, 40, 87, 75, 58, 49
4, 48, 58, 8, 99, 60, 91, 91, 46, 27
90, 85, 53, 60, 85, 46, 51, 33, 71, 92
13, 14, 60, 3, 94, 38, 2, 62, 33, 27
69, 33, 36, 66, 40, 63, 53, 34, 11, 4
56, 66, 86, 5, 95, 84, 69, 49, 49, 39
48, 91, 22, 35, 50, 64, 94, 35, 44, 97
70, 14, 28, 36, 68, 52, 69, 2, 54, 80
After (150):
57, 58, 58, 59, 59, 60, 60, 60, 61, 61
61, 62, 63, 64, 64, 65, 66, 66, 66, 68
68, 69, 69, 69, 69, 70, 71, 71, 71, 72
75, 75, 75, 76, 77, 78, 80, 81, 82, 83
84, 84, 85, 85, 85, 86, 87, 87, 90, 90
90, 90, 91, 91, 91, 92, 92, 94, 94, 94
95, 95, 95, 96, 96, 97, 97, 97, 98, 99
99, 56, 56, 55, 54, 54, 53, 53, 52, 51
50, 49, 49, 49, 48, 48, 47, 47, 46, 46
45, 44, 44, 40, 40, 39, 39, 39, 38, 38
38, 36, 36, 35, 35, 34, 34, 33, 33, 33
32, 29, 28, 28, 27, 27, 27, 27, 26, 25
23, 22, 22, 19, 19, 17, 14, 14, 13, 11
11, 10, 9, 8, 8, 8, 7, 7, 6, 5
5, 5, 4, 4, 3, 3, 2, 2, 2, 0
在 Linux 和 Mac 系统上,还有一个 qsort_r() 允许您将 N 作为值传递给比较器函数 — 但不幸的是,两个系统上的签名不同:
MacOS (BSD):
void qsort_r(void *base, size_t nel, size_t width, void *thunk,
int (*compar)(void *, const void *, const void *));
Linux:
void qsort_r(void *base, size_t nmemb, size_t size,
int (*compar)(const void *, const void *, void *), void *arg);
比较函数被赋予一个额外的参数,并且一个指针被传递给qsort(),该qsort() 被中继到比较器。但是,调用顺序不同。这是使用 MacOS 变体的代码 - Linux 变体的更改很简单。
/*
** Sort values greater than N first in ascending order, and values less
** than N last in descending order, with values equal to N in the middle.
*/
#include <stdio.h>
#include <stdlib.h>
static int comparator(void *v0, const void *v1, const void *v2)
{
int N = *(int *)v0;
int a = *(int *)v1;
int b = *(int *)v2;
if (a < N && b < N)
return (a < b) - (a > b); /* Descending */
else if (a > N && b > N)
return (a > b) - (a < b); /* Ascending */
else if (a > N && b < N)
return -1;
else if (a < N && b > N)
return +1;
else if (a == N && b > N)
return +1;
else if (b == N && a < N)
return +1;
else if (a == N && b < N)
return -1;
else if (b == N && a > N)
return -1;
else
return 0;
}
static void dump_array(const char *tag, size_t n, int *data)
{
printf("%s (%zu):\n", tag, n);
const char *pad = "";
size_t i;
for (i = 0; i < n; i++)
{
if (i > 0 && i % 10 == 0)
{
putchar('\n');
pad = "";
}
printf("%s%3d", pad, data[i]);
pad = ",";
}
if (i % 10 != 0)
putchar('\n');
putchar('\n');
}
int main(void)
{
int N = 56;
// random -n 100 0 99 | commalist -B 8 -n 10 -W 2
static int data[] =
{
22, 90, 87, 54, 81, 72, 68, 44, 82, 56,
45, 66, 97, 69, 84, 7, 47, 27, 77, 11,
99, 57, 95, 61, 90, 10, 98, 17, 29, 26,
5, 39, 8, 61, 38, 90, 92, 85, 19, 39,
76, 34, 97, 7, 23, 19, 27, 71, 8, 59,
64, 25, 78, 28, 6, 65, 32, 47, 96, 3,
55, 9, 75, 59, 5, 71, 83, 0, 95, 2,
38, 61, 96, 94, 75, 40, 87, 75, 58, 49,
4, 48, 58, 8, 99, 60, 91, 91, 46, 27,
90, 85, 53, 60, 85, 46, 51, 33, 71, 92,
13, 14, 60, 3, 94, 38, 2, 62, 33, 27,
69, 33, 36, 66, 40, 63, 53, 34, 11, 4,
56, 66, 86, 5, 95, 84, 69, 49, 49, 39,
48, 91, 22, 35, 50, 64, 94, 35, 44, 97,
70, 14, 28, 36, 68, 52, 69, 2, 54, 80,
};
enum { NUM_DATA = sizeof(data) / sizeof(data[0]) };
dump_array("Before", NUM_DATA, data);
qsort_r(data, NUM_DATA, sizeof(data[0]), &N, comparator);
dump_array("After", NUM_DATA, data);
return 0;
}
输出是一样的,因为输入的数据是一样的。
正如chqrlie 在comment 中所暗示的,可以通过注意您有一个升序 N+1 .. MAX 后跟一个降序 N .. MIN 来简化条件。这是对'plain qsort()'代码的改编,它生成具有值范围 0 .. 99 内的随机条目数的数组,并将断点 N 设置为随机值 20 .. 79。它对原始固定数据进行排序,然后是10组随机数据。它还包括一个函数check_order(),用于验证数据是否已排序。当比较器功能被破坏时,它会发现结果中的问题。
/* SO 6366-2914 */
/*
** Sort values greater than N first in ascending order, and values less
** than or equal to N last in descending order.
*/
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#define RETURN(v) \
do { \
if (print_return) \
printf("%2d: %2d <=> %2d --> %+d\n", __LINE__, a, b, (v)); \
return(v); \
} while (0)
static int print_return = 0;
static int N;
static int comparator(const void *v1, const void *v2)
{
int a = *(int *)v1;
int b = *(int *)v2;
if (a <= N && b <= N)
return((a < b) - (a > b)); /* Descending */
else if (a > N && b > N)
return((a > b) - (a < b)); /* Ascending */
else if (a > N)
{
assert(b <= N);
RETURN(-1);
}
else
{
assert(a <= N && b > N);
RETURN(+1);
}
}
static void dump_array(const char *tag, size_t n, const int *data)
{
printf("%s (%zu) N = %2d:", tag, n, N);
const char *pad = "";
size_t i;
for (i = 0; i < n; i++)
{
if (i % 10 == 0)
pad = "\n";
printf("%s%3d", pad, data[i]);
pad = ",";
}
putchar('\n');
putchar('\n');
}
static void check_order(size_t n, const int *data, int breakpoint)
{
size_t i = 0;
for (i = 1; i < n && data[i] > breakpoint; i++)
{
if (data[i-1] > data[i])
fprintf(stderr, "FAIL: A[%zu] = %2d out of ascending sequence with A[%zu] = %2d\n",
i-1, data[i-1], i, data[i]);
}
for ( ; i < n; i++)
{
if (data[i-1] < data[i])
fprintf(stderr, "FAIL: A[%zu] = %2d out of descending sequence with A[%zu] = %2d\n",
i-1, data[i-1], i, data[i]);
}
}
int main(void)
{
// random -n 100 0 99 | commalist -B 8 -n 10 -W 2
static int data[] =
{
22, 90, 87, 54, 81, 72, 68, 44, 82, 56,
45, 66, 97, 69, 84, 7, 47, 27, 77, 11,
99, 57, 95, 61, 90, 10, 98, 17, 29, 26,
5, 39, 8, 61, 38, 90, 92, 85, 19, 39,
76, 34, 97, 7, 23, 19, 27, 71, 8, 59,
64, 25, 78, 28, 6, 65, 32, 47, 96, 3,
55, 9, 75, 59, 5, 71, 83, 0, 95, 2,
38, 61, 96, 94, 75, 40, 87, 75, 58, 49,
4, 48, 58, 8, 99, 60, 91, 91, 46, 27,
90, 85, 53, 60, 85, 46, 51, 33, 71, 92,
13, 14, 60, 3, 94, 38, 2, 62, 33, 27,
69, 33, 36, 66, 40, 63, 53, 34, 11, 4,
56, 66, 86, 5, 95, 84, 69, 49, 49, 39,
48, 91, 22, 35, 50, 64, 94, 35, 44, 97,
70, 14, 28, 36, 68, 52, 69, 2, 54, 80,
};
enum { NUM_DATA = sizeof(data) / sizeof(data[0]) };
N = 56;
dump_array("Before", NUM_DATA, data);
qsort(data, NUM_DATA, sizeof(data[0]), comparator);
dump_array("After", NUM_DATA, data);
check_order(NUM_DATA, data, N);
srand(time(0));
for (size_t j = 0; j < 10; j++)
{
size_t n = rand() % 120 + 30;
N = rand() % 60 + 20;
for (size_t i = 0; i < n; i++)
data[i] = rand() % 100;
dump_array("Before", n, data);
qsort(data, n, sizeof(data[0]), comparator);
dump_array("After", n, data);
check_order(n, data, N);
}
return 0;
}
代码有一个粗略的调试工具,带有RETURN 宏。当print_return 变量设置为1 时,它会打印诊断信息。比较器中的所有返回语句都可以使用宏。在检查比较器函数中的哪些比较可以省略时很有用。
代码可以做得更彻底。它可以检查守恒属性(输出中的每个值都出现在输入中;输入中的每个值都出现在输出中)。它可以使用比srand()/rand() 组合更好的随机数生成器。它可以从标准输入(或命名文件)中读取数字,或者由命令行参数控制——以便可以在命令行上指定种子,或者打印出可重现性。所有这些修改都留给读者作为练习。