我如何在 C 中按字符对结构进行排序?
How i can sort struct by characters in C?
我使用了按数字对结构进行排序的策略,但是这个策略不起作用。
#include <stdio.h>
#define MAX 1000
typedef struct person_{
char name[100];
int age;
}person;
void scan_person(person *ptr);
void print_ind(person p);
int name_index_min(person *ptr, int N, int st);
void swap(person *x, person *y);
void sort_name_as(person *ptr, int N);
void sort_name_ds(person *ptr, int N);
int main () {
person individulas[MAX];
int n_ind;
puts("Enter Number of people:");
scanf("%d",&n_ind);
for (int i = 0; i < n_ind; i++)
scan_person(individulas + i);
printf("\n");
sort_name_as(individulas,n_ind);
for (int i = 0; i < n_ind; i++)
print_ind(individulas[i]);
puts("\n");
sort_name_ds(individulas,n_ind);
for (int i = 0; i < n_ind; i++)
print_ind(individulas[i]);
printf("\n");
return 0;
}
void scan_person(person *ptr){
printf("\nEnter Name & Surename :");
scanf("\n%99[^\n]", ptr->name);
printf("Enter Age :");
scanf ("%d%*c", &(ptr->age));
printf("\n");
}
void print_ind(person p){
printf("%s %d ", p.name, p.age);
}
int name_index_min(person *ptr, int N, int st){
int min = st;
for (int i = st+1; i < N; i++)
if(ptr[i].name < ptr[min].name)
min = i;
return min;
}
int name_index_max(person *ptr, int N, int st){
int max = st;
for (int i = st+1; i < N; i++)
if(ptr[i].name > ptr[max].name)
max = i;
return max;
}
void swap(person *x, person *y){
person temp = *x;
*x = *y;
*y = temp;
}
void sort_name_as(person *ptr, int N){
int aux;
for (int i = 0; i < N-1; i++){
aux = name_index_min(ptr, N, i);
if (aux != i)
swap(ptr + i, ptr + aux);
}
}
void sort_name_ds(person *ptr, int N){
int aux;
for (int i = 0; i < N-1; i++){
aux = name_index_max(ptr, N, i);
if (aux != i)
swap(ptr + i, ptr + aux);
}
}
示例如下:
输入:
输入 people:3
的数量
输入姓名:Jeffrey L. Davis
输入年龄:80
输入姓名:Yaseen Nur al Din Khoury
输入年龄:80
输入姓名:Joeri Ong
输入年龄:80
输出:
Joeri Ong 80 Jeffrey L. Davis 80 Yaseen Nur al Din Khoury 80 //升序
Yaseen Nur al Din Khoury 80 Jeffrey L. Davis 80 Joeri Ong 80 //降序
您的方法的主要问题是您比较的是名称的内存地址,而不是数组中包含的值。
当您按名称引用数组时,您实际上是在获取它的地址。我建议您使用 strcmp
来比较 min/max 搜索函数中的 name
数组。
快速查看您的代码后发现这是其中唯一的问题。
此方法的一种实现方式,输入稍微简化(使用 A
、B
和 C
作为名称)如下所示:https://onlinegdb.com/r1NkRiNKL.
每当你在 C 中遇到排序问题时,你应该立即思考 qsort
。它是一种高效灵活的排序功能,可以处理任何类型的需要排序的对象。通常吓跑新 C 程序员的是,您必须编写一个 compare()
函数来告诉 qsort
如何比较和排序数组中指向元素 的两个 指针。比较函数的原型是:
int compare (const void *a, const void *b)
所有 a
和 b
指针都是指向当前正在比较的数组中的两个元素的指针。您唯一的工作就是将它们转换为正确的类型,然后告诉 qsort
您希望如何比较它们。让我们看一下你的第一个函数,先按age
比较升序,然后如果年龄相等,接下来比较名字,这样所有年龄相同的患者都按字母顺序排序,从原型开始:
int compasc (const void *a, const void *b) /* qsort compare ascending by age */
{
不知道你的数组元素是什么类型(stuct person
这里),所以 a
和 b
将是 pointer-to struct person
。您的工作只是将指针 a
和 b
转换为类型 person*
,例如
const person *pa = a, *pb = b;
因此,您可以使用 person*
个指针 pa
和 pb
,而不是使用 void *
个指针 a
和 b
功能。如果年龄不同,比较年龄和return,例如
if (pa->age != pb->age)
return (pa->age > pb->age) - (pa->age < pb->age);
否则,年龄相等,因此您可以按 name
进行比较,例如
return (strcmp (pa->name, pb->name));
}
这就是您的比较功能所需要的。完整的是:
int compasc (const void *a, const void *b) /* qsort compare ascending by age */
{
const person *pa = a, *pb = b;
if (pa->age != pb->age)
return (pa->age > pb->age) - (pa->age < pb->age);
return (strcmp (pa->name, pb->name));
}
按年龄降序比较操作相同,除了 age
的比较会导致按年龄降序排序。无论如何,如果年龄相等,您将按姓名字母顺序排序。
你很好地定义了数组中 struct person
的最大数量的常量,但你也可以为 name
的大小声明一个常量,例如
#define MAXNM 100
#define MAXP 1000
typedef struct person {
char name[MAXNM];
int age;
} person;
(注意: '_'
已从 person_
中删除,因为 POSIX 对保留 begin/end 的名称很挑剔在下划线中 - 并且您的结构 tag 和 typdef 名称不需要不同)
在 main()
中,使用像 fgets()
或 POSIX 这样的面向行的输入函数,您可以更好地一次读取一行用户输入 getline()
。这确保了 stdin
中没有未读的不需要的字符,它们可能会在您下次尝试阅读时咬住您。所以只需声明一个简单的字符数组 buf
用作缓冲区来保存所有输入行。然后,您可以使用 sscanf()
从行中获取所需的内容来处理任何转换。要读取和存储数组中的所有数据,您可以执行以下操作:
int main (void) {
int n_ind = 0;
person individuals[MAXP] = {{ .name = "" }};
while (n_ind < MAXP) {
char buf[MAXP] = ""; /* buffer to hold line of input */
person tmp = { .name = "" }; /* temporary struct to fill */
fputs ("\nenter name & surname: ", stdout);
if (!fgets (buf, MAXNM, stdin))
return 1;
if (*buf == '\n')
break;
buf[strcspn (buf, "\n")] = 0; /* trim trailing '\n' */
strcpy (tmp.name, buf);
fputs ("enter age: ", stdout);
if (!fgets (buf, MAXP, stdin))
return 1;
if (sscanf (buf, "%d", &tmp.age) == 1) /* validate age conversion */
individuals[n_ind++] = tmp; /* add tmp to array update n_ind */
}
(注意: fgets()
还使您能够检查第一个字符是否为 '\n'
-- 允许您使用 在 name
提示符下单独输入 表示用户已完成输入——您不需要让他们键入他们将输入的数量。您只需不断添加名称直到用户在 name
提示符下单独按下 Enter。)
现在使用 qsort
排序变得微不足道,只需传递您的数组、元素的数量、每个元素的大小以及您希望 qsort
使用的比较函数,其余的由它完成, 例如
qsort (individuals, n_ind, sizeof *individuals, compasc); /* sort ascending */
这就是按年龄升序排列的内容 -- 您的 individuals
数组现在按年龄排序,然后按姓名排序。
将它与升序和降序放在一起,您将拥有:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAXNM 100
#define MAXP 1000
typedef struct person {
char name[MAXNM];
int age;
} person;
int compasc (const void *a, const void *b) /* qsort compare ascending by age */
{
const person *pa = a, *pb = b;
if (pa->age != pb->age)
return (pa->age > pb->age) - (pa->age < pb->age);
return (strcmp (pa->name, pb->name));
}
int compdesc (const void *a, const void *b) /* qsort compare descending by age */
{
const person *pa = a, *pb = b;
if (pa->age != pb->age)
return (pa->age < pb->age) - (pa->age > pb->age);
return (strcmp (pa->name, pb->name));
}
int main (void) {
int n_ind = 0;
person individuals[MAXP] = {{ .name = "" }};
while (n_ind < MAXP) {
char buf[MAXP] = ""; /* buffer to hold line of input */
person tmp = { .name = "" }; /* temporary struct to fill */
fputs ("\nenter name & surname: ", stdout);
if (!fgets (buf, MAXNM, stdin))
return 1;
if (*buf == '\n')
break;
buf[strcspn (buf, "\n")] = 0; /* trim trailing '\n' */
strcpy (tmp.name, buf);
fputs ("enter age: ", stdout);
if (!fgets (buf, MAXP, stdin))
return 1;
if (sscanf (buf, "%d", &tmp.age) == 1) /* validate age conversion */
individuals[n_ind++] = tmp; /* add tmp to array update n_ind */
}
qsort (individuals, n_ind, sizeof *individuals, compasc); /* sort ascending */
puts ("\nascending order by age:");
for (int i = 0; i < n_ind; i++)
printf (" %-24s %2d\n", individuals[i].name, individuals[i].age);
qsort (individuals, n_ind, sizeof *individuals, compdesc); /* sort descending */
puts ("\ndescending order by age:");
for (int i = 0; i < n_ind; i++)
printf (" %-24s %2d\n", individuals[i].name, individuals[i].age);
}
示例Use/Output
按年龄排序,如果年龄相等,则按姓名排序:
$ ./bin/individualsbyage
enter name & surname: Jeffrey L Davis
enter age: 80
enter name & surname: Yaseen Nur al Din Khoury
enter age: 80
enter name & surname: Joeri Ong
enter age: 80
enter name & surname:
ascending order by age:
Jeffrey L Davis 80
Joeri Ong 80
Yaseen Nur al Din Khoury 80
descending order by age:
Jeffrey L Davis 80
Joeri Ong 80
Yaseen Nur al Din Khoury 80
现在用一个简单的例子来说明如果年龄不同,就会得到正确的排序:
$ /bin/individualsbyage
enter name & surname: Jeffrey L Davis
enter age: 81
enter name & surname: Yaseen Nur al Din Khoury
enter age: 80
enter name & surname: Joeri Ong
enter age: 79
enter name & surname:
ascending order by age:
Joeri Ong 79
Yaseen Nur al Din Khoury 80
Jeffrey L Davis 81
descending order by age:
Jeffrey L Davis 81
Yaseen Nur al Din Khoury 80
Joeri Ong 79
qsort
除了针对快速排序进行优化之外的另一个好处是,如果不是 1,000,000 人(如果不是 1,000,000 人),它也已被使用和测试并得到验证。 (不是这样的,你只是碰巧在一夜之间拼凑起来)
检查一下,如果您还有其他问题,请告诉我。
我使用了按数字对结构进行排序的策略,但是这个策略不起作用。
#include <stdio.h>
#define MAX 1000
typedef struct person_{
char name[100];
int age;
}person;
void scan_person(person *ptr);
void print_ind(person p);
int name_index_min(person *ptr, int N, int st);
void swap(person *x, person *y);
void sort_name_as(person *ptr, int N);
void sort_name_ds(person *ptr, int N);
int main () {
person individulas[MAX];
int n_ind;
puts("Enter Number of people:");
scanf("%d",&n_ind);
for (int i = 0; i < n_ind; i++)
scan_person(individulas + i);
printf("\n");
sort_name_as(individulas,n_ind);
for (int i = 0; i < n_ind; i++)
print_ind(individulas[i]);
puts("\n");
sort_name_ds(individulas,n_ind);
for (int i = 0; i < n_ind; i++)
print_ind(individulas[i]);
printf("\n");
return 0;
}
void scan_person(person *ptr){
printf("\nEnter Name & Surename :");
scanf("\n%99[^\n]", ptr->name);
printf("Enter Age :");
scanf ("%d%*c", &(ptr->age));
printf("\n");
}
void print_ind(person p){
printf("%s %d ", p.name, p.age);
}
int name_index_min(person *ptr, int N, int st){
int min = st;
for (int i = st+1; i < N; i++)
if(ptr[i].name < ptr[min].name)
min = i;
return min;
}
int name_index_max(person *ptr, int N, int st){
int max = st;
for (int i = st+1; i < N; i++)
if(ptr[i].name > ptr[max].name)
max = i;
return max;
}
void swap(person *x, person *y){
person temp = *x;
*x = *y;
*y = temp;
}
void sort_name_as(person *ptr, int N){
int aux;
for (int i = 0; i < N-1; i++){
aux = name_index_min(ptr, N, i);
if (aux != i)
swap(ptr + i, ptr + aux);
}
}
void sort_name_ds(person *ptr, int N){
int aux;
for (int i = 0; i < N-1; i++){
aux = name_index_max(ptr, N, i);
if (aux != i)
swap(ptr + i, ptr + aux);
}
}
示例如下:
输入:
输入 people:3
的数量输入姓名:Jeffrey L. Davis
输入年龄:80
输入姓名:Yaseen Nur al Din Khoury
输入年龄:80
输入姓名:Joeri Ong
输入年龄:80
输出:
Joeri Ong 80 Jeffrey L. Davis 80 Yaseen Nur al Din Khoury 80 //升序
Yaseen Nur al Din Khoury 80 Jeffrey L. Davis 80 Joeri Ong 80 //降序
您的方法的主要问题是您比较的是名称的内存地址,而不是数组中包含的值。
当您按名称引用数组时,您实际上是在获取它的地址。我建议您使用 strcmp
来比较 min/max 搜索函数中的 name
数组。
快速查看您的代码后发现这是其中唯一的问题。
此方法的一种实现方式,输入稍微简化(使用 A
、B
和 C
作为名称)如下所示:https://onlinegdb.com/r1NkRiNKL.
每当你在 C 中遇到排序问题时,你应该立即思考 qsort
。它是一种高效灵活的排序功能,可以处理任何类型的需要排序的对象。通常吓跑新 C 程序员的是,您必须编写一个 compare()
函数来告诉 qsort
如何比较和排序数组中指向元素 的两个 指针。比较函数的原型是:
int compare (const void *a, const void *b)
所有 a
和 b
指针都是指向当前正在比较的数组中的两个元素的指针。您唯一的工作就是将它们转换为正确的类型,然后告诉 qsort
您希望如何比较它们。让我们看一下你的第一个函数,先按age
比较升序,然后如果年龄相等,接下来比较名字,这样所有年龄相同的患者都按字母顺序排序,从原型开始:
int compasc (const void *a, const void *b) /* qsort compare ascending by age */
{
不知道你的数组元素是什么类型(stuct person
这里),所以 a
和 b
将是 pointer-to struct person
。您的工作只是将指针 a
和 b
转换为类型 person*
,例如
const person *pa = a, *pb = b;
因此,您可以使用 person*
个指针 pa
和 pb
,而不是使用 void *
个指针 a
和 b
功能。如果年龄不同,比较年龄和return,例如
if (pa->age != pb->age)
return (pa->age > pb->age) - (pa->age < pb->age);
否则,年龄相等,因此您可以按 name
进行比较,例如
return (strcmp (pa->name, pb->name));
}
这就是您的比较功能所需要的。完整的是:
int compasc (const void *a, const void *b) /* qsort compare ascending by age */
{
const person *pa = a, *pb = b;
if (pa->age != pb->age)
return (pa->age > pb->age) - (pa->age < pb->age);
return (strcmp (pa->name, pb->name));
}
按年龄降序比较操作相同,除了 age
的比较会导致按年龄降序排序。无论如何,如果年龄相等,您将按姓名字母顺序排序。
你很好地定义了数组中 struct person
的最大数量的常量,但你也可以为 name
的大小声明一个常量,例如
#define MAXNM 100
#define MAXP 1000
typedef struct person {
char name[MAXNM];
int age;
} person;
(注意: '_'
已从 person_
中删除,因为 POSIX 对保留 begin/end 的名称很挑剔在下划线中 - 并且您的结构 tag 和 typdef 名称不需要不同)
在 main()
中,使用像 fgets()
或 POSIX 这样的面向行的输入函数,您可以更好地一次读取一行用户输入 getline()
。这确保了 stdin
中没有未读的不需要的字符,它们可能会在您下次尝试阅读时咬住您。所以只需声明一个简单的字符数组 buf
用作缓冲区来保存所有输入行。然后,您可以使用 sscanf()
从行中获取所需的内容来处理任何转换。要读取和存储数组中的所有数据,您可以执行以下操作:
int main (void) {
int n_ind = 0;
person individuals[MAXP] = {{ .name = "" }};
while (n_ind < MAXP) {
char buf[MAXP] = ""; /* buffer to hold line of input */
person tmp = { .name = "" }; /* temporary struct to fill */
fputs ("\nenter name & surname: ", stdout);
if (!fgets (buf, MAXNM, stdin))
return 1;
if (*buf == '\n')
break;
buf[strcspn (buf, "\n")] = 0; /* trim trailing '\n' */
strcpy (tmp.name, buf);
fputs ("enter age: ", stdout);
if (!fgets (buf, MAXP, stdin))
return 1;
if (sscanf (buf, "%d", &tmp.age) == 1) /* validate age conversion */
individuals[n_ind++] = tmp; /* add tmp to array update n_ind */
}
(注意: fgets()
还使您能够检查第一个字符是否为 '\n'
-- 允许您使用 在 name
提示符下单独输入 表示用户已完成输入——您不需要让他们键入他们将输入的数量。您只需不断添加名称直到用户在 name
提示符下单独按下 Enter。)
现在使用 qsort
排序变得微不足道,只需传递您的数组、元素的数量、每个元素的大小以及您希望 qsort
使用的比较函数,其余的由它完成, 例如
qsort (individuals, n_ind, sizeof *individuals, compasc); /* sort ascending */
这就是按年龄升序排列的内容 -- 您的 individuals
数组现在按年龄排序,然后按姓名排序。
将它与升序和降序放在一起,您将拥有:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAXNM 100
#define MAXP 1000
typedef struct person {
char name[MAXNM];
int age;
} person;
int compasc (const void *a, const void *b) /* qsort compare ascending by age */
{
const person *pa = a, *pb = b;
if (pa->age != pb->age)
return (pa->age > pb->age) - (pa->age < pb->age);
return (strcmp (pa->name, pb->name));
}
int compdesc (const void *a, const void *b) /* qsort compare descending by age */
{
const person *pa = a, *pb = b;
if (pa->age != pb->age)
return (pa->age < pb->age) - (pa->age > pb->age);
return (strcmp (pa->name, pb->name));
}
int main (void) {
int n_ind = 0;
person individuals[MAXP] = {{ .name = "" }};
while (n_ind < MAXP) {
char buf[MAXP] = ""; /* buffer to hold line of input */
person tmp = { .name = "" }; /* temporary struct to fill */
fputs ("\nenter name & surname: ", stdout);
if (!fgets (buf, MAXNM, stdin))
return 1;
if (*buf == '\n')
break;
buf[strcspn (buf, "\n")] = 0; /* trim trailing '\n' */
strcpy (tmp.name, buf);
fputs ("enter age: ", stdout);
if (!fgets (buf, MAXP, stdin))
return 1;
if (sscanf (buf, "%d", &tmp.age) == 1) /* validate age conversion */
individuals[n_ind++] = tmp; /* add tmp to array update n_ind */
}
qsort (individuals, n_ind, sizeof *individuals, compasc); /* sort ascending */
puts ("\nascending order by age:");
for (int i = 0; i < n_ind; i++)
printf (" %-24s %2d\n", individuals[i].name, individuals[i].age);
qsort (individuals, n_ind, sizeof *individuals, compdesc); /* sort descending */
puts ("\ndescending order by age:");
for (int i = 0; i < n_ind; i++)
printf (" %-24s %2d\n", individuals[i].name, individuals[i].age);
}
示例Use/Output
按年龄排序,如果年龄相等,则按姓名排序:
$ ./bin/individualsbyage
enter name & surname: Jeffrey L Davis
enter age: 80
enter name & surname: Yaseen Nur al Din Khoury
enter age: 80
enter name & surname: Joeri Ong
enter age: 80
enter name & surname:
ascending order by age:
Jeffrey L Davis 80
Joeri Ong 80
Yaseen Nur al Din Khoury 80
descending order by age:
Jeffrey L Davis 80
Joeri Ong 80
Yaseen Nur al Din Khoury 80
现在用一个简单的例子来说明如果年龄不同,就会得到正确的排序:
$ /bin/individualsbyage
enter name & surname: Jeffrey L Davis
enter age: 81
enter name & surname: Yaseen Nur al Din Khoury
enter age: 80
enter name & surname: Joeri Ong
enter age: 79
enter name & surname:
ascending order by age:
Joeri Ong 79
Yaseen Nur al Din Khoury 80
Jeffrey L Davis 81
descending order by age:
Jeffrey L Davis 81
Yaseen Nur al Din Khoury 80
Joeri Ong 79
qsort
除了针对快速排序进行优化之外的另一个好处是,如果不是 1,000,000 人(如果不是 1,000,000 人),它也已被使用和测试并得到验证。 (不是这样的,你只是碰巧在一夜之间拼凑起来)
检查一下,如果您还有其他问题,请告诉我。