【问题标题】:Sorting Subarrays in C Sorts Entire Array Instead?在C中对子数组进行排序而不是对整个数组进行排序?
【发布时间】:2018-06-06 22:44:05
【问题描述】:

所以在我写的这个小游戏中,我有一副n_C 卡片,每张卡片都有一个唯一的整数值。 有n_P玩家,每个人手里都拿着n_H牌。

甲板和手共享一个数组,如下所示: 玩家i_P 的手牌包含从deck[i_P * n_Hdeck[i_P * (n_H + 1) - 1] 的所有牌。所有玩家加起来总持有少于n_C 的牌。

现在我想对每个玩家的手牌进行排序,而不是整个牌组(显然,这会在玩家的手牌之间移动一些牌)。我是这样处理的:

void sort_hands(char * deck, size_t n_P) {
    for (size_t i_P = 0; i_P < n_P; i_P++)
        sort_cards(deck + i_P * n_H, n_H);
}

然后,使用插入排序(这个函数可以很好地对整个数组进行排序):

void sort_cards(char * cards, size_t n) {
    for (size_t i = 0; i < n; i++) {
        for (size_t j = i - 1; j >= 0; j--) {
            if (cards[j] > cards[j + 1])
                card_swap(cards, j, j + 1);
            else
                break;
    }
}

*注意:card_swap完全符合您的预期,它只有 3 条语句。

n_Cn_H都是#define的常量,n_P是一个局部变量。 当我使用洗牌后的牌组和参数n_C = 104, n_H = 10, n_P = 3 运行上述代码时,我得到:

0:74 31 53 46 42 75 72 77 70 49

1:76 86 99 78 11 94 61 14 41 87

2:40 26 92 5 9 3 66 63 101 98

在子数组排序之前:

0:3 5 9 11 31 42 46 49 53 74

1:61 70 75 72 76 77 78 86 94 99

2:14 26 40 41 63 66 87 92 98 101

之后。正如我们所看到的,有些牌在不应该的时候移动了手。但是这套牌并没有被所有牌手分类(参见例如玩家 2 手中的14。这怎么可能?在我看来,我的编码技能并没有错,但编译器似乎把我弄乱了。

我在 VS15 C++ 编译器和 GCC 中都观察到了这种行为。

【问题讨论】:

  • "in C" - 那你为什么把它标记为C++
  • for (size_t j = i - 1; j &gt;= 0; j--) 中,当i == 0 外循环的第一次迭代时会发生什么? size_t 类型是无符号的。这看起来像是一个糟糕的冒泡排序。
  • “我的编码能力没有问题,但编译器似乎在惹我。”永远怀疑这一点。
  • 在我看来,我的编码技能并没有错, -- 所以你是说全世界成千上万的人和公司都在使用编译器无法正确执行您的简单代码?
  • 在你的 C 编程生涯的这个阶段,假设如果编译器打算警告你一个问题,这意味着你有一个严重的错误。编译器没有错。也许在 30 年后你将有能力换一种思维方式,但我知道当编译器抱怨时我必须假设我有错,而且这种假设错误的情况非常罕见。

标签: c++ c arrays sorting


【解决方案1】:

我在 Mac 上使用 GCC 7.2.0 和命令行编译了您的 sort_cards() 函数(在提供了缺少的 } 之后):

gcc -O3 -g -std=c11 -Wall -Wextra -Werror -Wmissing-prototypes \
    -Wstrict-prototypes -c cards53.c

使用这些选项(关键是-Wextra),它会立即发出警告:

cards53.c:31:34: error: comparison of unsigned expression >= 0 is always true [-Werror=type-limits]
         for (size_t j = i - 1; j >= 0; j--)

这表明存在严重问题。特别是在外循环的第一次迭代中,i0,所以i - 1 是一个非常大的数字。坦率地说,您声称该函数将成功对整个数组进行排序的说法是错误的。它不会。而且我不会运行无法使用显示的命令行编译的代码。

如果您修复了该功能,那么您的代码就可以了。我用过:

#include <stdio.h>

#define n_H 10
#define n_C 104

static inline void card_swap(char *deck, int i1, int i2)
{
    char t = deck[i1];
    deck[i1] = deck[i2];
    deck[i2] = t;
}

static void sort_cards(char *cards, size_t n)
{
    for (size_t i = 1; i < n; i++)
    {
        for (size_t j = i; j-- > 0; )
        {
            if (cards[j] > cards[j + 1])
                card_swap(cards, j, j + 1);
            else
                break;
        }
    }
}

static void sort_hands(char *deck, size_t n_P)
{
    for (size_t i_P = 0; i_P < n_P; i_P++)
        sort_cards(deck + i_P * n_H, n_H);
}

static void dump_hands(const char *tag, const char *deck, size_t n_P)
{
    printf("%s:\n", tag);
    for (size_t p = 0; p < n_P; p++)
    {
        printf("Player %zu:", p + 1);
        const char *hand = deck + p * n_H;
        for (int i = 0; i < n_H; i++)
            printf(" %3d", hand[i]);
        putchar('\n');
    }
}

int main(void)
{
    char deck[n_C] =
    {
        74,  31,  53,  46,  42,  75,  72,  77,  70,  49,
        76,  86,  99,  78,  11,  94,  61,  14,  41,  87,
        40,  26,  92,   5,   9,   3,  66,  63, 101,  98,
    };
    int n_P = 3;
    dump_hands("Before", deck, n_P);
    sort_hands(deck, n_P);
    dump_hands("After", deck, n_P);
    return 0;
}

数组使用您提供的样本数据进行初始化;余数全为零,但这对本练习无关紧要。

样本输出:

Before:
Player 1:  74  31  53  46  42  75  72  77  70  49
Player 2:  76  86  99  78  11  94  61  14  41  87
Player 3:  40  26  92   5   9   3  66  63 101  98
After:
Player 1:  31  42  46  49  53  70  72  74  75  77
Player 2:  11  14  41  61  76  78  86  87  94  99
Player 3:   3   5   9  26  40  63  66  92  98 101

检查显示每个子数组都排序正确。

【讨论】:

  • 在我现在看来,我的编码技能受到了打击,我什至无法正确调试。感谢您的帮助!
  • 让编译器指出你的错误会很有帮助。这就是我向您展示我使用的编译选项的原因;他们让我避免了很多麻烦。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2019-03-06
  • 2018-04-20
  • 2021-06-18
  • 1970-01-01
  • 2011-04-23
  • 2013-12-24
  • 2013-08-05
相关资源
最近更新 更多