Сортировка std :: list с помощью итераторов
Возможно ли отсортировать часть списка (подмножество списка), определенного итераторами типа std::sort
?
т.е. с помощью std::list
единственный доступный доступ через метод (http://en.cppreference.com/w/cpp/container/list/sort), я хотел бы иметь возможность сортировать часть списка из его итераторы используют std::sort
. например
std::sort(listItrStart, listItrEnd, [](T& a, T& b){ return a.something() < b.something()});
Я понимаю, что итераторы становятся недействительными, как только операция перемещения выполняется над элементом, который, как я полагаю, означает, что список не может быть отсортирован с помощью итераторов без повторной итерации в нужное место до следующего "сравнения"?
В каком случае лучше всего сортировать подпрограммы списков без заполнения другого контейнера для этого процесса (если таковые имеются)?
Большое спасибо.
Ответы
Ответ 1
Заполнение другого контейнера неизбежно. Но вам не нужно перемещать или копировать свои собственные данные. Вы можете использовать std::list::splice
для извлечения и повторной установки узлов, которые вы хотите обработать, в отсортированном порядке.
using list_t = std::list<widget>;
void process(list_t& in, list_t::const_iterator begin, list_t::const_iterator end) {
list_t sorter;
sorter.splice(sorter.end(), in, begin, end);
sorter.sort();
in.splice(end, sorter);
}
Функция передает узлы, которые вы хотите сортировать, в список сортировщиков (первым аргументом итератора является позиция, перед которой вставлены узлы, в этом случае конец списка).
Сортировщик сортируется (очевидно), а затем отсортированный контент переносится обратно в исходный список, точно в исходный поддиапазон, который он первоначально заселял.
Как прокомментировал @TC Следующий шаг - обобщить его. Его можно превратить в шаблон, подобный этому:
template<class List, class Compare = std::less<>>
void sort_subrange(List& in,
typename List::const_iterator begin,
typename List::const_iterator end,
Compare c = {}) {
List sorter(in.get_allocator());
sorter.splice(sorter.end(), in, begin, end);
[[maybe_unused]] ScopeGuard sg([&]() { in.splice(end, sorter); });
sorter.sort(std::move(c));
}
Здесь также используется компаратор, и sorter
построен с копией входного распределителя для максимальной типичности. Склеивание назад выполняется в области защиты по нашему выбору, чтобы поддерживать случай, когда функция сравнения выбрасывается, поэтому наши базы теперь покрыты.
Вот живой пример, с наивной и несколько глупой реализацией охранника области для целей экспозиции.
Ответ 2
Возможно ли отсортировать часть списка (подмножество списка), определенного итераторами типа std :: sort?
Я предполагаю, что вы имели в виду std :: list :: sort. Внедрение Visual Studio 2015 делает это и без необходимости заполнения другого контейнера. Это сортировка с наименьшим слиянием, которая менее эффективна, чем предыдущая сортировка спуска вверх, но она позволяет избежать выделения памяти, которую предыдущая версия сделала с того, что предыдущая версия выделяла небольшой массив списков. Код Psuedo будет выглядеть примерно так:
right = std::next(right, 1); // right = end of sub-list
size = std::distance(left, right);
left = MyListSort(list, left, right, size);
right = std::next(left, size-1); // right = last element of sub-list
// ...
MyListSort(list, left, right, size)
{
if(size < 2)
return left;
mid = std::next(left, size/2);
MyListSort(list, left, mid, size/2);
MyListSort(list, mid, right, size-size/2);
firstloop = true;
newleft = left;
while(true){
if(*left <= *mid){
if(++left == mid)
return newleft;
} else {
if(firstloop)
newleft = mid;
list.splice(left, list, mid);
if(++mid == right)
return newleft;
}
firstloop = false;
}
}