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
}