qsort для более чем одного элемента в структуре - PullRequest
4 голосов
/ 08 марта 2012

Мне поручено, чтобы пользователь вводил n строк, содержащих месяц, день и год, в виде «12 января 99».

Я должен отсортировать список дат в хронологическом порядке, используя qsort сначала по годузатем днем, потом месяцем.

Моя проблема в том, что я не уверен в том, как выполнить сортировку по нескольким индексам.Я сделал это в течение года, который работает хорошо, но после этого я не знаю, как сделать qsort по дням, так как, конечно, он будет просто сортировать его по дням, но годы снова запутаются?* РЕДАКТИРОВАТЬ: Итак, у меня работает сортировка, я просто пытаюсь преобразовать целое число обратно в строковое представление месяца при печати отсортированного списка.Вот код, я получаю сообщение об ошибке «индекс массива не целое число».

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

typedef int (*compfn)(const void*, const void*);

struct date
{
    int month;
    int day; //The day of the month (e.g. 18)
    int year; //The year of the date    
};

char* months[]= {
   "January", "February",
   "March", "April",
   "May", "June",
   "July", "August",
   "September", "October",
   "November", "December"};


int getMonth(char tempMonth[])
{
    int i = 0;
    for(i = 0; i<12; i++)
    {
            if(tempMonth == months[i]) return i;
    }
    return 0;
}

char getStringMonth(struct date month)
{
    return months[month];
}

int sortDates(struct date *elem1, struct date *elem2)
{
    if (elem1 -> year < elem2 -> year)
    {
        return -1;
    }
    else 

    if (elem1->year > elem2->year)
        {
        return 1;
    }


    if ( elem1->month < elem2->month )
    {
            return -1;
    }
    else 

    if ( elem1->month > elem2->month )
        {
        return 1; 
    }


    if ( elem1->day < elem2->day )
    {list
            return -1;
    }
        else 

    if ( elem1->day > elem2->day )
        {
        return 1;
    } 
        else

    return 0;
}

main()
{
    int n;
    int i;
    char tempMonth[255]; //Used to store the month until checked

    scanf("%d", &n);list

    struct date *list;

    list = (struct date *)malloc((n * sizeof(struct date)));

    for(i = 0; i < n; i++)
    {
        scanf("%s %d %d", tempMonth, &list[i].day, &list[i].year);
        list[i].month = getMonth(tempMonth);
    }

    qsort(list, sizeof(list), sizeof(struct date), (compfn)sortDates);

    for(i = 0; i < n; i++)
    {
        printf("%s %d %d", getStringMonth(list[i].month), list[i].day, list[i].year);
    }

}

Ответы [ 5 ]

5 голосов
/ 08 марта 2012

Не забудьте free память, которую вы malloc 'd.

В следующем коде я предположил, что месяц хранится как число, но я вижу в вашей структуре, что это недело.Я бы преобразовал введенный месяц из строки в число, чтобы упростить процесс сортировки.

int sortDates(struct date* elem1, struct date* elem2)
{

    if ( elem1->year < elem2->year)
        return -1;
    else if ( elem1->year > elem2->year )
        return 1;


    /* here you are sure the years are equal, so go on comparing the months */

    if ( elem1->month < elem2->month )
        return -1;
    else if ( elem1->month > elem2->month )
        return 1; 

    /* here you are sure the months are equal, so go on comparing the days */

    if ( elem1->day < elem2->day )
        return -1;
    else if ( elem1->day > elem2->day )
        return 1; 
    else
        return 0; /* definitely! */

}

Также обратите внимание на это объявление: char month[9];, правда, сентябрь - 9 символов, но вам нужнотерминатор char '\0' в C, чтобы закрыть строку.Чтобы избежать подобных проблем, я бы объявил массив со всеми месяцами, чтобы разрешить проверку и преобразовать месяц из строки в число:

char* allMonths[]=
   "January", "February",
   "March", "April",
   "May", "June",
   "July", "August",
   "September", "October",
   "November", "December";

char tmpMonth[255];
scanf("%s %d %d", tmpMonth, &list[i].day, &list[i].year);
/* convert tmpMonth to a number by finding it in the allMonths and throw error if not found */
2 голосов
/ 08 марта 2012

Вместо возврата 0, когда два года равны, просто скопируйте и вставьте логику и примените ее к месяцам.Затем еще раз на несколько дней.Вот и все.

Кстати, если вам нужна хронологическая сортировка, вы должны сначала отсортировать годы, затем месяцы, а затем дни.Не дни, а месяцы.

И вы должны хранить месяцы в виде чисел, а не строк: это будет проще для хронологической сортировки.

Наконец, я бы назвал функцию compareDates, а не sortDates.

1 голос
/ 08 марта 2012

То, что у вас есть, хорошо для года (основной ключ).Вам просто нужна небольшая адаптация, чтобы использовать месяц (минорный ключ), когда год равен, и вы можете расширить его на столько уровней клавиш, сколько захотите.

Псевдокод только для домашней работы, я 'я боюсь:

def compare (date1, date2):
    if date1.year > date2.year:
        return 1
    if date1.year < date2.year:
        return -1

    # Years are equal here.

    if date1.month > date2.month
        return 1
    if date1.month < date2.month
        return -1

    # Years and months are equal here.

    if date1.day > date2.day
        return 1
    if date1.day < date2.day
        return -1

    # Years, months and days are all equal here.

    return 0

Выполнив одну «многоуровневую» функцию сравнения, подобную этой, вам не нужно будет беспокоиться о сортировке по месяцам с разбивкой по годам, поскольку она сортируется по дням в течение месяца в течение года.

И, как вы можете видеть, я не большой поклонник:

if condition:
    return something
else:
    carry on

идиомы.else совершенно не нужен и может вызвать огромные уровни отступов там, где они не нужны.Я предпочитаю:

if condition:
    return something

carry on

Фактический метод сортировки лучше всего сделать, превратив названия этих месяцев в числовые значения, чтобы сравнения работали лучше.Это, вероятно, лучше оставить для другого вопроса, но вы могли бы собрать что-то с массивом строк:

char *months[] = {"January", "February", ... "December"};

и циклом for для преобразования имени в значение в диапазоне от 0 до 11.

1 голос
/ 08 марта 2012

Вместо возврата 0 на последнем else, сравните другое поле:

else {
    if (elem1->day < elem2->day) {
        return -1;
    }
    else if (elem1->day > elem2->day) {
        return 1;
    }
    else {
        //compare months
    }
}

Основная идея:

  1. Проверьте годы, если они не совпадают с возвращаемым результатом (как у вас)
  2. Если годы совпадают, проверьте дни, если они не совпадают, результат возврата
  3. Если дни тоже совпадают, проверьте месяц.
0 голосов
/ 08 марта 2012

Вы должны изменить свою функцию сравнения sortDates и принять решение, основываясь на днях, когда годы равны, и на основании месяца, когда дни и месяцы равны.

...