С++ сортировка со структурами

Мне трудно решить эту проблему, которая требует своего рода имен клиентов, идентификаторов клиентов и, наконец, причитающейся суммы. У меня есть вся программа, но я не могу определить последний прототип, необходимый для сортировки. у меня есть структура под названием Customers, и я также предоставлю часть int main(). Мне просто нужна помощь, чтобы начать работу над прототипом SortData().

struct Customers {
    string Name;
    string Id;
    float OrderAmount;
    float Tax;
    float AmountDue;
};

const int MAX_CUSTOMERS = 1000;
bool MoreCustomers(int);
Customers GetCustomerData();
void OutputResults(Customers [], int);
void SortData(const int, const int, Customers []);

int main() {
    Customers c[MAX_CUSTOMERS]; 
    int Count = 0;      
    do {
      c[Count++] = GetCustomerData();   
    } while (MoreCustomers(Count));     


    for (int i = 0; i < Count; i++) {
        c[i].Tax = 0.05f * c[i].OrderAmount;        
        c[i].AmountDue = c[i].OrderAmount + c[i].Tax;   
    }

    SortData(0, Count, c);     //0:Sorts by customer name       
    OutputResults(c, Count);            
    GeneralSort(1, Count, c);   //1:Sorts by ID     
    OutputResults(c, Count);        
    GeneralSort(2, Count, c);   //2: Sorts by amount due        
    OutputResults(c, Count);        

    return 0;                       
}


void SortData(const int SortItem, const int count, CustomerProfile c[]) {
     //0: Sort by name
    //1: Sort by ID
    //3: Sort by amount due
}

person Community    schedule 17.05.2009    source источник


Ответы (4)


Вы должны использовать стандартную функцию сортировки C++, std::sort, объявленную в заголовке <algorithm>.

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

bool customer_sorter(Customer const& lhs, Customer const& rhs) {
    if (lhs.Name != rhs.Name)
        return lhs.Name < rhs.Name;
    if (lhs.Id != rhs.Id)
        return lhs.Id < rhs.Id;
    return lhs.AmountDue < rhs.AmountDue;
}

Теперь передайте эту функцию вашему вызову sort:

std::sort(customers.begin(), customers.end(), &customer_sorter);

Это предполагает, что у вас есть контейнер STL (а не массив, как в примере кода) с именем customers, содержащий клиентов.

person Chris Jester-Young    schedule 17.05.2009

Часто упускается из виду, что вы действительно можете использовать функции диапазона STL с массивами на основе C, как в вашем примере. Таким образом, вам на самом деле не нужно переходить на использование контейнера на основе STL (я не буду здесь обсуждать достоинства этого :-)).

Итак, основываясь на ответе Криса, вы можете вызвать сортировку следующим образом:

std::sort( customers, customers+Count, &customer_sorter);
person CodeBuddy    schedule 17.05.2009

Вам нужно только написать функцию сравнения, которая сравнивает два типа CustomerProfile. Если у вас есть эта функция, вы можете использовать любую сортировку STL (см. http://www.sgi.com/tech/stl/sort.html или http://msdn.microsoft.com/en-us/library/ecdecxh1(VS.80).aspx) или старая сортировка C: http://en.wikipedia.org/wiki/Qsort_(C_Standard_Library). Я бы посоветовал не писать собственный алгоритм сортировки, если только это не домашнее задание. Ваше сравнение зависит от технологии, которую вы предпочитаете использовать, оно может выглядеть примерно так:

int CompareCustomerProfile(
   const CustomerProfile* pC1,
   const CustomerProfile* pC2)
{
 int result = strcmp(pC1->name, pC2->name);
 if (0 != result) return result; 

  result = strcmp(pC1->ID, pC2->ID);
  if (0 != result) return result;

  if (pC1->amountDue < pC2->amountDue) return -1;
 if (pC1->amountDue > pC2->amountDue) return 1;

  return 0
}

это предполагает, что тип «строка» в вашем примере — это char*. Если вы используете Unicode или многобайтовые типы, то, очевидно, необходимо использовать соответствующее Unicode или многобайтовое сравнение. Тогда вы просто вызовете алгоритм с вашей функцией сравнения. Например. используя сортировку:

qsort(c, Count, sizeof(CustomerProfile), CompareCustomerProfiler).

Теперь, если это является домашним заданием, вы не должны спрашивать здесь, как это сделать...

person Remus Rusanu    schedule 17.05.2009

Я предполагаю, что вы новичок в программировании или на C++, так что вот что вы, вероятно, ищете:

#include <search.h> // for the qsort()

int
CompareByName( const void *elem1, const void *elem2 )
{
  return ((Customers*)elem1)->Name > ((Customers*)elem2)->Name? 1 : -1;
}

int
CompareByOrderAmount( const void *elem1, const void *elem2 )
{
  return ((Customers*)elem1)->OrderAmount > ((Customers*)elem2)->OrderAmount? 1 : -1;
}

void SortData( int SortItem, int count, Customers customers[] )
{
  switch (SortItem) {
  case 0:
    qsort(customers, count, sizeof(Customers), CompareByName);
    break;
  case 1:
    qsort(customers, count, sizeof(Customers), CompareByOrderAmount);
    break;
  // ...
  }
}

void test()
{
  Customers cust[10];

  cust[0].Name = "ten";
  cust[1].Name = "six";
  cust[2].Name = "five";
  SortData( 0, 3, cust );
  cout << cust[0].Name << endl;
  cout << cust[1].Name << endl;
  cout << cust[2].Name << endl;
}
person Nick Dandoulakis    schedule 17.05.2009
comment
Ваш ответ явно является ответом в стиле C, без каких-либо качеств C ++ (за исключением использования перегруженного оператора › для строк). Если бы это был вопрос C (и вы изменили сравнение строк на strcmp), ваш ответ был бы полезен, но в нынешнем виде он совершенно бесполезен для вопроса. Во-первых, в стиле C++ сортировка должна осуществляться с использованием std::sort, а не qsort. (Кстати, даже при использовании qsort ваша функция сравнения должна обрабатывать случай, когда два элемента равны, и возвращать 0.) - person Chris Jester-Young; 18.05.2009
comment
Да, ты прав. Но использование простой структуры также соответствует стилю C. Использование STL может быть немного загадочным для начинающих. В любом случае, у меня нет проблем с удалением моего ответа. - person Nick Dandoulakis; 18.05.2009
comment
Моя функция сравнения должна обрабатывать случай равных элементов, правда. Однако такой случай встречается редко, и фактическая проблема заключается в том, что при сортировке массивов лучше использовать массивы указателей, а не массивы структур. Так что любой хороший ответ субъективен. - person Nick Dandoulakis; 18.05.2009
comment
Договоритесь об отсутствии контейнеров с целыми конструкциями, чтобы сделать сортировку менее дорогой. Я не могу говорить об идиоматическом использовании массивов в C++, но не рекомендуется использовать (голые) указатели в контейнерах STL, потому что таким образом труднее отслеживать время жизни объекта. (shared_ptr рекомендуется, когда нужны указатели, например, чтобы избежать нарезки или для предотвращения копирования большого количества объектов (как в этом случае), или при использовании с некопируемым типом.) - person Chris Jester-Young; 18.05.2009
comment
На самом деле, в этой заметке, AFAIK qsort можно использовать только с типами POD (стандарт, кажется, ничего не говорит по этой теме, но Google может использовать qsort с типами, отличными от pod). В примере OP в структуре используются некоторые типы, отличные от POD (в частности, std::string), так что это еще один аргумент в пользу хранения указателей вместо целых объектов. (Однако интеллектуальные указатели, такие как shared_ptr, также не являются типами POD, поэтому использование qsort, как правило, не рекомендуется, несмотря ни на что.) - person Chris Jester-Young; 18.05.2009
comment
Для справки: более поздние версии стандарта C++ не так молчат о том, можно ли использовать qsort с типами, отличными от POD: open-std.org/jtc1/sc22/wg21/docs/papers/2006/n2062.html - person Chris Jester-Young; 18.05.2009