Как я могу отсортировать структуру по символам в C? - PullRequest
0 голосов
/ 25 апреля 2020

Я использовал стратегию для сортировки структуры по числам, но эта стратегия не работает.

#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);
    }
}

Вот пример:

Ввод:

Введите число люди: 3

Введите имя и имя: Джеффри Л. Дэвис
Введите возраст: 80

Введите имя и имя: Ясен Нур аль Дин Хури
Введите возраст: 80

Введите имя и имя: Джоэри Онг
Введите возраст: 80

Выход:

Джоэри Онг 80 Джеффри Л. Дэвис 80 Ясин Нур аль Дин Хури 80 // порядок вознесения

Ясин Нур аль Дин Хури 80 Джеффри Л. Дэвис 80 Джоэри Онг 80 // порядок убывания

Ответы [ 2 ]

2 голосов
/ 26 апреля 2020

Всякий раз, когда вы сталкиваетесь с проблемой сортировки в 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;

Таким образом, вместо void * указателей a & b, у вас есть person* указатели pa и pb для работы в функции. Если возраст отличается, сравните возраст и верните, например,

    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;

( note: '_' был удален из person_, поскольку POSIX требователен к резервированию имен, начинающихся с / заканчивается на подчеркивания - и вам не нужно, чтобы ваши теги struct и 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 */
    }

( note: fgets() также дает вам возможность проверить, является ли первый символ '\n' - позволяет вам использовать только Enter в приглашении name в качестве указания на то, что пользователь завершил ввод - вам не нужно, чтобы они вводили, сколько они будут вводить. Вы просто продолжаете добавлять имена, пока пользователь не нажмет Ввод в одиночку по приглашению name.

Теперь сортировка становится тривиальной с 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);

}

Пример использования / Вывод

Сортировка по возрасту, если возраст равен, то по имени:

* 109 9 *

Теперь краткий пример, показывающий, что если возраст различается, получаются правильные сортировки:

$ /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 для быстрой сортировки, это также может быть использовано и проверено 100 000 человек (если не 100 000 человек) и подтверждено. (не совсем так, что вы, случается, бьетесь в одночасье)

Посмотрите вещи и дайте мне знать, если у вас есть дополнительные вопросы.

0 голосов
/ 25 апреля 2020

Основная проблема вашего подхода состоит в том, что вы сравниваете адрес памяти имен вместо значений, содержащихся в массивах.

Когда вы ссылаетесь на массив по имени, вы фактически получаете его адрес. Я предлагаю вам использовать strcmp для сравнения name массивов в ваших функциях поиска min / max.

Быстрый просмотр вашего кода показал, что это единственная проблема в нем.

Здесь реализована реализация этого подхода с несколько упрощенным вводом (используются A, B и C в качестве имен): https://onlinegdb.com/r1NkRiNKL.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...