【问题标题】:Sort an array of structs by different members按不同成员对结构数组进行排序
【发布时间】:2020-06-17 12:55:59
【问题描述】:

我需要使用 qsort 对一个结构数组进行排序,每个结构都有不同数据类型的成员。

typedef struct {
 char* name;
 int age;
 float wage;
} employee;

问题是,我是否必须为它们中的每一个编写 3 个不同的比较器函数,还是有一个很好的方法来实现 1 个函数?

【问题讨论】:

  • 简短回答:是的。
  • 如果你想按不同的成员排序,那么你需要为每个成员一个比较器。
  • 我必须为用户提供按姓名对员工进行排序的功能
  • 好的,谢谢
  • 使用单个比较器(可移植地)执行此操作的唯一方法是在调用qsort 之前设置一个全局变量,然后检查比较器中的全局变量以查看要比较的成员。非便携式解决方案是qsort_r 函数,它允许将附加信息传递给比较器。有关qsort_r 的可移植性问题的一些背景信息,请参阅this question

标签: c arrays sorting struct qsort


【解决方案1】:

适用于对employee 类型的对象进行排序的比较函数将接收指向employee 对象的参数。每个对象都有多种类型的成员会影响您如何实现这样的功能,但这本身并不能说明您需要多个功能的任何理由。

int compare_employees(const void *e1, const void *e2) {
    const employee *employee1 = e1;
    const employee *employee2 = e2;

    // ... code to compare *employee1 with *employee2 ...
}

但是,每个比较函数都定义了一种特定的对象排序方式。如果您想根据不同的标准进行排序,那么您需要为每种排序方法提供不同的比较功能。这与成员是否都具有相同的数据类型无关。

【讨论】:

  • 我想说我需要能够按员工姓名、年龄等分别对员工进行排序
  • 这个答案解决了这个问题,@IlyaKozel。
  • 我没有读到最后,抱歉。谢谢你帮助我
【解决方案2】:

这个函数应该完成你所寻求的:

int compare_employees(const void * e1, const void * e2) {
    const employee * emp1 = (employee*)e1;
    const employee * emp2 = (employee*)e2;

    int ret1 = strcmp(emp1->name, emp2->name);

    if (ret1 == 0) {
        if (emp1->age == emp2->age) {
            float ret2 = emp1->wage - emp2->wage;

            if (ret2 == 0)
                return 0;
            else if (ret2 > 0)
                return 1;
            else
                return -1;
        } else {
            return emp1->age - emp2->age;
        }
    } else {
        return ret1;
    }
}

首先考虑字母顺序,然后是年龄,最后是工资。

当然,我假设您想按升序对元素进行排序。如果您想按降序对它们进行排序,您所要做的就是翻转每次比较的值:

float ret2 = emp2->wage - emp1->wage; 例如按工资降序对员工进行排序。

此外,如果您希望排序功能具有不同的优先级(即工资比姓名的字母顺序更具决定性,但后者仍然比年龄更具决定性),请在 if 语句中以不同的方式排列它们:

if (ret1 == 0) {
    int ret2 = strcmp(emp1->name, emp2->name);
    if (ret2 == 0)
        return emp1->age - emp2->age;
    else
        return ret2;
} else if (ret1 > 0) {
    return 1;
} else {
    return -1;
}

【讨论】:

  • 因此,要执行不同的排序顺序,您的函数需要不同的主体,或者(更好,等效地)不同的函数——而不仅仅是一个函数。
  • @JonathanLeffler 它们不是“块”,而不是函数吗?
  • 好吧,如果你有一个机制,比如一个全局变量来指示要执行哪个“块”(compound statement?),那么它可能是“块”。但是全局变量很难看——使用单独的比较器函数会更简单(并且可以说性能更高,尽管几乎无法衡量)。
【解决方案3】:

在任何情况下,您都需要为每个数据成员编写一个单独的函数来对数组进行排序,因为在函数中您需要比较具体数据成员的值。

但是,您可以编写一个通用函数,为调用 qsort 提供所需的比较函数。

这是一个演示程序。

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

typedef struct {
 char* name;
 int age;
 float wage;
} employee;

int cmp_by_name( const void *a, const void *b )
{
    const employee *first  = a;
    const employee *second = b;

    return strcmp( first->name, second->name ); 
}

int cmp_by_age( const void *a, const void *b )
{
    const employee *first  = a;
    const employee *second = b;

    return ( second->age < first->age ) - ( first->age < second->age );
}

int cmp_by_wage( const void *a, const void *b )
{
    const employee *first  = a;
    const employee *second = b;

    return ( second->wage < first->wage ) - ( first->wage < second->wage );
}


enum SortBy { ByName, ByAge, ByWage };

int ( *select( enum SortBy sort_by ) )( const void *, const void * )
{
    int ( *cmp[] )( const void *, const void * ) = 
    {
        cmp_by_name, cmp_by_age, cmp_by_wage
    };

    switch ( sort_by )
    {
        default:
        case ByName:
            return cmp[ByName];
        case ByAge:
            return cmp[ByAge];
        case ByWage:
            return cmp[ByWage];
    }
}

int main(void) 
{
    enum { N = 3 };
    employee e[N] =
    {
        { "Tom", 18, 3500.0f },
        { "Bob", 26, 4500.0f },
        { "Jim", 28, 4000.0f }
    };

    for ( size_t i = 0; i < N; i++ )
    {
        printf( "%s, %d, %f\n", e[i].name, e[i].age, e[i].wage );
    }
    putchar( '\n' );

    qsort( e, N, sizeof( employee ), select( ByName ) );    

    for ( size_t i = 0; i < N; i++ )
    {
        printf( "%s, %d, %f\n", e[i].name, e[i].age, e[i].wage );
    }
    putchar( '\n' );

    qsort( e, N, sizeof( employee ), select( ByAge ) ); 

    for ( size_t i = 0; i < N; i++ )
    {
        printf( "%s, %d, %f\n", e[i].name, e[i].age, e[i].wage );
    }
    putchar( '\n' );

    qsort( e, N, sizeof( employee ), select( ByWage ) );    

    for ( size_t i = 0; i < N; i++ )
    {
        printf( "%s, %d, %f\n", e[i].name, e[i].age, e[i].wage );
    }
    putchar( '\n' );

    return 0;
}

程序输出是

Tom, 18, 3500.000000
Bob, 26, 4500.000000
Jim, 28, 4000.000000

Bob, 26, 4500.000000
Jim, 28, 4000.000000
Tom, 18, 3500.000000

Tom, 18, 3500.000000
Bob, 26, 4500.000000
Jim, 28, 4000.000000

Tom, 18, 3500.000000
Jim, 28, 4000.000000
Bob, 26, 4500.000000

【讨论】:

  • 善用const(a&gt;b)-(a&lt;b)成语。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2020-04-08
  • 1970-01-01
  • 2019-10-28
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多