【问题标题】:Sorting an array of structs Efficiently有效地对结构数组进行排序
【发布时间】:2016-10-16 17:03:42
【问题描述】:

我正在使用qsort 对结构数组进行排序,并且我正在寻找一种更有效的方法来通过对数组进行部分排序来提取前三个最高分数。我的结构如下所示:

typedef struct {
    double score;
    int player_num;
} player_t;

我已经分配了一个这样的结构数组:

player_t *players = malloc(SIZE * sizeof(player_t));

我对这个结构数组进行排序的方法是首先对分数进行排序,如果分数相同,则按玩家编号排序。

我的代码与qsort 如下所示:

#include <stdio.h>
#include <stdlib.h>

#define SIZE 6
#define MAX 3

int scorecmp(const void *a, const void *b);
int ComparePlayerScore( const void* ap , const void* bp );
int CompareInt( const int a , const int b );

typedef struct {
    double score;
    int player_num;
} player_t;

int
main(int argc, char *argv[]) {
    int i;

    int player_numbers[] = {1, 2, 3, 4, 5, 6};
    double scores[] = {0.765, 0.454, 0.454, 0.345, 0.643, 0.532};

    player_t *players = malloc(SIZE * sizeof(player_t));

    for (i = 0; i < SIZE; i++) {
        players[i].score = scores[i];
        players[i].player_num = player_numbers[i];
    }

    qsort(players, SIZE, sizeof(*players), ComparePlayerScore);

    for (i = 0; i < MAX; i++) {
        printf("Player %d: Score: %.3f\n", players[i].player_num, players[i].score);
    }

    free(players);

    return 0;
}

int ComparePlayerScore( const void* ap , const void* bp ) {
    const player_t* const a = ap;
    const player_t* const b = bp;

    if( a->score == b->score ){
        return CompareInt( a->player_num , b->player_num );
    }
    else if( a->score > b->score ) {
        return -1;
    }
    else {
        return 1;
    }
}

int CompareInt( const int a , const int b ) {
    if( a < b ){
        return -1;
    }
    else if( a > b ){
        return 1;
    }

    return 0;
}

我只是在寻找另一种方法来做到这一点,但更有效的方法是提取前 3 名的分数以及各自的球员号码。就像我可以提取数组的前三个元素而不必每次都先对整个数组进行排序一样。

【问题讨论】:

  • 我猜这是recently answered question的后续行动。
  • 是的,我一直在对此进行试验,并尝试使其与qsort 一起使用,但如果不先对整个数组进行排序,我似乎无法做到这一点。似乎使用结构数组变得更加困难。
  • 我想说不要担心这个级别的性能。您的数组只有 6 个大小,因此不值得优化可能多余的三个比较。保持代码简单。而且,如果您后来测量程序的这一部分是迄今为止最慢的,那么您才应该优化它。
  • 您需要(部分)对数组进行排序,还是只查找得分最高的 3 个元素的索引值?
  • 是的,就是这样,我很好奇阵列变大时的性能。但我不确定如何从理论上优化它。

标签: c arrays sorting struct


【解决方案1】:

这是我的奉献。既然你有#define MAX 来表示你想找到多少最高分,我已经考虑过了。

程序对数据进行一次传递,并为每条记录传递最顶层的 1 次。我使用了一个固定的数组,你必须适应你的需要。

#include <stdio.h>

#define SIZE 6
#define MAX 3

typedef struct {
    double score;
    int player_num;
} player_t;

int main(int argc, char *argv[])
{
    player_t players[SIZE] = {{2.0, 2}, {4.0, 5}, {1.0, 4}, {1.0, 1}, {5.0, 3}, {4.0, 6}};
    int order[MAX+1] = {0};
    int found = 1;
    int i, j;
    int bigger;

    for(i = 1; i < SIZE; i++) {
        bigger = 0;
        for(j = 0; j < found; j++) {
            if(players[i].score > players[ order[j] ].score) {
                bigger = 1;
                break;
            }
            else if(players[i].score == players[ order[j] ].score && 
                    players[i].player_num < players[ order[j] ].player_num) {
                bigger = 1;
                break;
            }
        }
        if(bigger) {
            memmove(order + j + 1, order + j, (found - j) * sizeof order[0]);
            order[j] = i;
            if(found < MAX) {
                found++;
            }
        }
    }

    for(i = 0; i < found; i++) {
        printf("%d %f\n", players[ order[i] ].player_num, players[ order[i] ].score); 
    }
    return 0;
}

程序输出:

3 5.000000
5 4.000000
6 4.000000

【讨论】:

    【解决方案2】:

    只是一个简单的尝试,请参阅http://ideone.com/8A1lnP 上的在线演示。

    struct top3_players {
      player_t *top[3];
    };
    
    void top3_determine(struct top3_players *top, player_t *players, size_t n) {
      player_t *top1 = NULL;
      player_t *top2 = NULL;
      player_t *top3 = NULL;
      for (size_t i = 0; i < n; i++) {
        player_t *player = &players[i];
        if (top1 == NULL || ComparePlayerScore(player, top1) < 0) {
          top3 = top2;
          top2 = top1;
          top1 = player;
        } else if (top2 == NULL || ComparePlayerScore(player, top2) < 0) {
          top3 = top2;
          top2 = player;
        } else if (top3 == NULL || ComparePlayerScore(player, top3) < 0) {
          top3 = player;
        }
      }
      top->top[0] = top1;
      top->top[1] = top2;
      top->top[2] = top3;
    }
    

    【讨论】:

    • 虚拟比较器是否具有某种教育意义?
    • 不,只是为了让我的代码在粘贴到 ideone 时可以自行编译。我删了。
    【解决方案3】:

    这里有一个解决方案,它通过存储指向前三个分数的指针来跟踪它们。当您添加球员或更改分数时,会调用更新函数以保持最高分数列表为最新。这里的优点是您只需要遍历三个元素的列表。我修改了你的原始代码来演示它是如何工作的:

    #include <stdio.h>
    #include <stdlib.h>
    
    #define SIZE 6
    #define MAX 3
    
    typedef struct {
        double score;
        int player_num;
    } player_t;
    
    void update_topscores(player_t **top, player_t *pplayer);
    
    int
    main(int argc, char *argv[]) {
        int i;
    
        int player_numbers[] = {1, 2, 3, 4, 5, 6};
        double scores[] = {0.765, 0.454, 0.454, 0.345, 0.643, 0.532};
    
        player_t *players = malloc(SIZE * sizeof(player_t));
        player_t **topscores = calloc(3, sizeof(player_t *));
    
        for (i = 0; i < SIZE; i++) {
            players[i].score = scores[i];
            players[i].player_num = player_numbers[i];
            update_topscores(topscores, &(players[i]));
        }
    
        for (i = 0; i < SIZE; i++) {
            printf("Player %d: Score: %.3f\n",
                   players[i].player_num, players[i].score);
        }
    
        puts("Top Scores");
        for (i = 0; i < 3; i++) {
            printf("Player %d: Score: %.3f\n",
                   topscores[i]->player_num, topscores[i]->score);    
        }
    
        /* Change score for player 4 */
        players[3].score = 0.755;
        update_topscores(topscores, &(players[3]));
        puts("New Top Scores");
        for (i = 0; i < 3; i++) {
            printf("Player %d: Score: %.3f\n",
                   topscores[i]->player_num, topscores[i]->score);
        }
    
        free(players);
        free(topscores);
    
        return 0;
    }
    
    void update_topscores(player_t **top, player_t *pplayer)
    {
        int i;
        player_t *temp;
    
        for (i = 0; i < 3; i++)
            if (top[i] == NULL) {
                top[i] = pplayer;
                return ;
            }
        for (i = 0; i < 3; i++) {
            if ((pplayer->score) > (top[i]->score)) {
                temp = top[i];
                top[i] = pplayer;
                update_topscores(top, temp);
                break;
            }
        }
    
        return ;
    }
    

    可以看到有一个函数update_topscores()用来更新列表。在上面的代码中,这是在构建初始玩家列表时使用的。然后,当玩家 4 的分数更新时,再次调用该函数,从而产生一个新的最高分数列表。该列表未排序,但如果需要,这样做很容易,您只需对包含三个条目的列表进行排序。

    为指向玩家指针topscores 的三个指针进行了额外分配,这当然必须在最后释放。

    这是一个示例输出:

    Player 1: Score: 0.765
    Player 2: Score: 0.454
    Player 3: Score: 0.454
    Player 4: Score: 0.345
    Player 5: Score: 0.643
    Player 6: Score: 0.532
    Top Scores
    Player 1: Score: 0.765
    Player 5: Score: 0.643
    Player 6: Score: 0.532
    New Top Scores
    Player 1: Score: 0.765
    Player 4: Score: 0.755
    Player 5: Score: 0.643
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-01-15
      • 1970-01-01
      • 2017-01-24
      • 1970-01-01
      相关资源
      最近更新 更多