C++ двойная сортировка данных с несколькими элементами

У меня есть несколько записей данных, которые содержат следующую информацию: идентификационный номер ФИО1 дата ФИО2

можно поместить это в такую структуру:

struct entry {
  int id_number;
  string name1;
  int date;
  string name2;
}

в моих данных, у меня много таких записей и я хотел бы, чтобы отсортировать. Во-первых, я хочу Сортировать по алфавиту на основе name1, а затем сортировать по дате. Однако, сортировка по дате является подмножеством в алфавитном порядке сортировки, например, если у меня есть две записи с одинаковым имя1, я тогда хочу заказать эти записи по дате. Кроме того, когда я сортирую, я хочу, чтобы элементы записи оставались вместе, поэтому все четыре значения идут вместе.

мои вопросы следующие:

1) Какой тип структуры данных я должен использовать для хранения этих данных, чтобы я мог сохранить набор из четырех элементов вместе, когда я сортирую любой из них?

2) каков самый быстрый способ сделать эту сортировку (с точки зрения количества времени для написания кода). В идеале, я хочу использовать что-то вроде сортировки в алгоритмы.h поскольку он уже встроен.

3) имеет ли STL некоторую встроенную структуру данных, которая может эффективно обрабатывать двойную сортировку, которую я описал?

3 ответов


структура у вас есть в порядке, за исключением того, что вы можете добавить перегрузку operator< для сравнения. Здесь я делаю сравнение "сравнить по имени, затем дата":

// Add this as a member function to `entry`.
bool operator<(entry const &other) const {
    if (name1 < other.name1)
        return true;
    if (name1 > other.name1)
        return false;

    // otherwise name1 == other.name1
    // so we now fall through to use the next comparator.

    if (date < other.date)
        return true;
    return false;
}

[Edit: то, что требуется, называется "строгим слабым порядком". Если вы хотите подробно рассказать о том, что означает и какие альтернативы возможны, Дэйв Абрахамс написал довольно подробный пост на C++ Next об этом.

в случае выше, мы начинаем путем сравнивать name1 поля из двух. Если a<b, то мы сразу возвращаем true. В противном случае, мы проверяем,a>b, и если это так, мы возвращаем false. В этот момент мы исключили a<b и a>b, так мы определили, что a==b, в этом случае мы проверяем даты -- if a<b, мы возвращаем true. В противном случае мы возвращаем false-либо даты равны, либо b>a, одно из которых означает тест на a<b - Это ложь. Если сортировка должна разобраться (без каламбура), какой из них имеет место, он может вызвать функции с аргументами поменялись. Имена будут по-прежнему равны, так что все будет сводиться к датам-если мы ошибемся, даты будут равны. Если мы получим true на замененных датах, то то, что началось как вторая дата, на самом деле больше. ]

на operator< вы определить в структуре определяет порядок, который будет использоваться по умолчанию. Когда/если вы хотите, вы можете указать другой порядок сортировки использовать:

struct byid { 
    bool operator<(entry const &a, entry const &b) { 
        return a.id_number < b.id_number;
    }
};

std::vector<entry> entries;

// sort by name, then date
std::sort(entries.begin(), entries.end());

// sort by ID
std::sort(entries.begin(), entries.end(), byid());

эта структура данных должна работать нормально. Вы должны переопределить оператор less than, затем вы можете просто вставить их все в карту, и они будут отсортированы. вот дополнительная информация об операторах сравнения для карты

Update: при дальнейшем размышлении я бы использовал набор, а не карту, потому что нет необходимости в значении. Но вот доказательство, что он все еще работает

доказательство это работает:

#include<string>
#include<map>
#include<stdio.h>
#include <sstream>


using namespace std;

struct entry {
  int m_id_number;
  string m_name1;
  int m_date;
  string m_name2;

  entry(  int id_number, string name1, int date, string name2) :
      m_id_number(id_number),
      m_name1(name1),
      m_date(date),
      m_name2(name2)
  {

  }

  // Add this as a member function to `entry`.
  bool operator<(entry const &other) const {
      if (m_name1 < other.m_name1)
          return true;
      if (m_name2 < other.m_name2)
          return true;
      if (m_date < other.m_date)
          return true;
      return false;
  }

  string toString() const
  {
      string returnValue;

      stringstream out;
      string dateAsString;

      out << m_date;
      dateAsString = out.str();

      returnValue = m_name1 + " " + m_name2 + " " + dateAsString;

      return returnValue;
  }
};


int main(int argc, char *argv[])
{
    string names1[] = {"Dave", "John", "Mark", "Chris", "Todd"};
    string names2[] = {"A", "B", "C", "D", "E", "F", "G"};

    std::map<entry, int> mymap;
    for(int x = 0; x < 100; ++x)
    {
        mymap.insert(pair<entry, int>(entry(0, names1[x%5], x, names2[x%7]), 0));
    }

    std::map<entry, int>::iterator it = mymap.begin();
    for(; it != mymap.end() ;++it)
    {
        printf("%s\n ", it->first.toString().c_str());
    }
    return 0;
}

на самом деле вы можете использовать объект-функцию для реализации ваших критериев сортировки

предположим, что вы хотите сохранить записи в наборе

//EntrySortCriteria.h
class EntrySortCriteria
{
    bool operator(const entry &e1, const entry &e2) const
    {
         return e1.name1 < e2.name1 || 
                (!(e1.name1 < e2.name1) && e1.date < e2.date))
    }
}

//main.cc
#include <iostream>
#include "EntrySortCriteria.h"

using namespace std;
int main(int argc, char **argv)
{

    set<entry, EntrySortCriteria> entrySet;
    //then you can put entries into this set, 
    //they will be sorted automatically according to your criteria
    //syntax of set:
    //entrySet.insert(newEntry);
    //where newEntry is a object of your entry type    
}