【问题标题】:sort elements in array that are greater than n in ascending order and remaining in descending in C在C中对数组中大于n的元素进行升序排序并以降序排序
【发布时间】:2020-08-30 23:00:01
【问题描述】:

我需要在 C 中按升序和降序对数组进行排序。

大于 n 的值需要排在第一位并升序,任何小于 n 的值都将排在最后并按降序排列。会有未知数量的输入,下面的代码不反映实际输入,仅作为示例。

#include <stdio.h>

void main(){

int n = 4;
int arr[] = {5, 6, 2, 8, 3}

结果输出将是 5,6,8,3,2

我的解决方案:

if (arr[0] >n){
  for (i = 0; i < arrSize; i++){
     for (j = 0; i < arrSize; j++){
     if ( arr[j] < arr[i] && arr[i] < n){
       int temp = arr[i];
       arr[i] = arr[j];
       arr[j] = temp;
     }

这部分适用于第一部分(数组元素> n)我在后半部分遇到问题。从前面的代码继续我的解决方案:

     else if(arr[i] < n){
      int temp2 = arr[n-1];
      arr[n-1] = arr[i];
      arr[i] = temp2;
     }
     

不幸的是,这仅适用于 n 下的元素,如果它们被连续放置在输入中,否则排序失败。

非常感谢我在此排序中可以阅读的任何帮助或对主题的参考。抱歉,如果有人问过这个问题,我在 stackoverflow 搜索中没有找到类似的东西

【问题讨论】:

  • 分解问题:1. 编写一个比较两个元素的函数(这是这里的关键部分)。 2. 使用任何排序算法(例如预定义的qsort)使用您的自定义比较器(而不是&lt;)对数组进行排序。
  • 这个问题是不是在网上某个地方?
  • "值 大于 n 需要排在第一位并升序,任何值 小于 n 将排在最后并降序顺序。” 任何值等于 n 应该去哪里?
  • 据我所知问题不在网上
  • 等于 n 应该提示用户选择另一个输入,它在我的实际代码中但我觉得没有必要添加

标签: arrays c sorting


【解决方案1】:

您可以通过 3 个步骤解决您的问题:

  • 首先将数组划分为大于n 的元素和小于或等于n 的元素。
  • 然后按升序对前半部分进行排序
  • 并按降序对后半部分进行排序
void swap(int *array, int i, int j) {
    int temp = array[i];
    array[i] = array[j];
    array[j] = temp;
}

void split_sort(int *array, int size, int n) {
    int i, j, middle;

    /* partition the array using n as a pivot value */
    for (i = 0, j = size; i < j;) {
        while (i < j && array[i] > n)
            i++;
        while (j > i && array[j - 1] <= n)
            j--;
        if (i >= j)
            break;
        swap(array, i++, --j);
    }
    middle = i;
    /* sort the left half in ascending order with a simplistic algorithm */
    for (i = 1; i < middle; i++) {
        for (j = 0; j < i; j++) {
            if (array[j] > array[i]) {
                swap(array, i, j);
            }
        }
    }
    /* sort the right half in descending order with a simplistic algorithm */
    for (i = middle + 1; i < size; i++) {
        for (j = middle; j < i; j++) {
            if (array[j] < array[i]) {
                swap(array, i, j);
            }
        }
    }
}

【讨论】:

  • 感谢您的评论让我以不同的方式思考问题,我将尝试执行我自己的代码再次感谢
【解决方案2】:

您可以使用标准的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;
}

输出是一样的,因为输入的数据是一样的。

正如chqrliecomment 中所暗示的,可以通过注意您有一个升序 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() 组合更好的随机数生成器。它可以从标准输入(或命名文件)中读取数字,或者由命令行参数控制——以便可以在命令行上指定种子,或者打印出可重现性。所有这些修改都留给读者作为练习。

【讨论】:

  • 有趣的方法!似乎可以通过使用a &lt;= Nb &lt;= N 而不是a &lt; Nb &lt; N 来简化比较功能,并删除额外的测试。
  • @chqrlie — 你是对的。我已经包含了程序的第三个版本,它有一个更简单的比较器,并且有代码来检查数据是否正确排序。修改后的代码中只有 3 行条件,而不是 8 行条件——要简单得多!
【解决方案3】:

试试下面的 -

  1. 对单次迭代使用快速排序枢轴技术(可能需要自定义实现)将给定数组拆分为两个数组(一个需要升序,另一个需要降序)。此支点与您的问题陈述中的支点相同。
  2. 使用快速排序或等效方法对左半边升序和右半边降序进行排序。
  3. 排序算法具有“稳定”(保留元素的顺序)和“就地”(不使用额外的内存/RAM)方面/行为。你没有在你的问题中指出这一点,所以我假设不需要“稳定”的行为。如果需要,您可能必须修改第一步并使用合并排序技术(一次迭代)进行分区,然后使用合并排序按升序和降序对数组进行排序。
  4. 注意 - 对于降序排序,您可以简单地先对数组进行升序排序,然后再将其反转!
  5. 更新 #1:正如 cmets 中的 chqrlie 所指出的,在反转时需要小心,以保持元素的“稳定”行为(特别是,不要对相等的元素应用反转)!

【讨论】:

  • 你可以简单地先对数组进行升序排序,然后再逆序排序!注意,这种方法即使升序排序也不稳定。
  • 我不熟悉快速排序枢轴,所以我会先阅读一下,谢谢
  • @chqrlie - 猜你是对的。我们需要保留相等元素的顺序。谢谢!
  • @chqrlie - 无论如何看起来更像是 OP 的作业,因此一些自动评分测试用例可能会抓住它并提供相应的建议。我希望没有真正的伤害!无论如何都会编辑我的答案。
  • @RavindraHV:没问题,稳定性似乎超出了问题的范围,我只是想强调一下反转排序数组的潜在问题。
猜你喜欢
  • 2017-03-05
  • 2015-08-30
  • 2016-02-01
  • 1970-01-01
  • 2021-12-06
  • 1970-01-01
  • 1970-01-01
  • 2020-06-06
  • 1970-01-01
相关资源
最近更新 更多