std: контейнер c ++ перемещается вперед

Я ищу std-контейнер, например std :: list, который может эффективно перемещать элемент вперед:

a-b-c-d-e

переместите «b» вперед:

a-c-d-e-b

Такую функцию в контейнерах std нет. Поэтому я думаю, что я должен объединить функцию remove и push_front, но кто-нибудь может найти лучшую идею?

Заранее спасибо.

8
nl ja de
Решение «быстрого» списка изменяет множество указателей (по 8 байт в 64-битном коде) на случайных позициях в потенциально нераспакованной памяти. Вектор из пяти элементов (как в примере) может поместиться в одну строку кеша и быть очень быстрым.
добавлено автор Bo Persson, источник
Что означает «эффективно»?
добавлено автор Jon, источник
Хороший вопрос ... Я имею в виду лучше, чем удалить его и создать его спереди. Если бы мне пришлось реализовать его с нуля (в C), я мог бы просто сделать a.next точкой «c», c.prev указывать на «a», b.prev на «e», b.next на NULL , передняя точка - «b», а e.next - «b» ... это будет эффективно; но повторная реализация контейнера требует слишком много времени;)
добавлено автор Jav, источник

2 ответы

В std :: vector вы можете использовать std :: rotate , который имеет линейную сложность

#include 
#include 
#include 
#include 

std::vector v = { 0, 1, 2, 3, 4 };

int main()
{
   std::copy(v.begin(), v.end(), std::ostream_iterator(std::cout, ",")); 
   std::cout << "\n";

  //swap ranges [1, 2) and [2, 5)
   auto it = std::next(v.begin(), 1);//O(1)
   auto rb = std::next(it);
   auto re = v.end();
   std::rotate(it, rb, re);//O(N)

   std::copy(v.begin(), v.end(), std::ostream_iterator(std::cout, ","));   
   std::cout << "\n";
}

В std :: list вы можете использовать функцию-член splice , которая (с учетом итераторов) имеет постоянную сложность

#include 
#include 
#include 
#include 

std::list v = { 0, 1, 2, 3, 4 };

int main()
{
   std::copy(v.begin(), v.end(), std::ostream_iterator(std::cout, ",")); 
   std::cout << "\n";

   auto it = std::next(v.begin(), 1);//O(N)
   auto rb = std::next(it);
   auto re = v.end();   
   v.splice(it, v, rb, re);//O(1)

   std::copy(v.begin(), v.end(), std::ostream_iterator(std::cout, ","));
   std::cout << "\n";   
}

NOTE: the last element is conventially denoted as back in the STL containers, and the first element as front. For std::vector, getting iterators to a certain element is constant time, and swapping is linear time. For std::list, getting iterators is linear time, but splicing into the same list is constant time. However, the much better memory caching behavior of vector is also important as this benchmark by Stroustrup shows.

UPDATE: Several commenters mentioned simply swapping elements: this only applies if you want to transform a-b-c-d-e into a-e-c-d-b. In that case, use std::iter_swap on any container you like. For the transformation of a-b-c-d-e into a-c-d-e-b, use std::rotate or list::splice.

14
добавлено
@MichaelWild Но с постоянным фактором намного выше, чем, вероятно, будет медленнее. (Конечно, это зависит от размера контейнера и перемещаемого типа).
добавлено автор James Kanze, источник
@MichaelWild Готовьтесь к удивлению. Стирание элемента из std :: list , а затем вставка его в начало, включает в себя две копии элемента plus бесплатно и выделение. Освобождение и выделение памяти может потребовать больше времени, чем требуется для копирования нескольких сотен int или double . Для коллекции менее тысячи int , std :: vector , вероятно, будет бить std :: list для этой операции. Даже решение, связанное с спеканием, вероятно, коснется нескольких разных страниц, где, как и для меньших наборов данных, решение с vector может никогда не оставить пропущенную кеш.
добавлено автор James Kanze, источник
@rhalbersma Решение rotate изменяет порядок оставшихся элементов. Если это приемлемо, просто замена первого элемента намного эффективнее.
добавлено автор James Kanze, источник
Это более эффективно, чем удаление и вставка одного элемента?
добавлено автор Lieuwe, источник
@MichaelWild tnx, см. Обновленный ответ. Обратите внимание также на презентацию Stroustrup, которая дает неожиданные результаты для вставки в std :: vector .
добавлено автор TemplateRex, источник
@JamesKanze Нет, это неверно: std :: rotate вращает все поддиапазоны. см. liveworkspace.org/code/QItmN$17 Последовательность 0,1,2,3,4 поворачивается на 0,2,3,4,1. Это именно тот тип преобразования, который запросил ОП. См. Обновление для ответа.
добавлено автор TemplateRex, источник
@Lieuwe См. Обновление для ответа.
добавлено автор TemplateRex, источник
Использование std :: list , а операция удаления и нажатия будет иметь постоянную сложность ...
добавлено автор Michael Wild, источник
Удаление ссылки из std :: list и вставка ее в другое место действительно очень дешево. Я был бы очень удивлен, если бы реализация std :: vector была быстрее, за исключением патологических случаев.
добавлено автор Michael Wild, источник

Если вам не нужно поддерживать порядок других элементов, то самое простое решение, несомненно, просто для замены элемент, который вы хотите, с первым элементом в контейнере. Эта будет эффективнее с контейнерами all .

В противном случае std :: list предлагает операцию splice , которая может использоваться. Что-то вроде следующего, я думаю:

void
moveToFront( 
    std::list& list,
    std::list::iterator element )
{
    if ( element != list.begin() ) {
        list.splice( list.begin(), list, element, std::next( element ) );
    }
}

Это должно закончиться только несколькими операциями указателя и no . С другой стороны, std :: list может быть очень медленным в общий (из-за его плохой локальности); Я бы очень измерил тщательно против наивной реализации, используя std :: vector , чтобы убедиться, что это победа во всем мире. Исключить все копии здесь не может быть победой, если итерация, чтобы найти элемент, который вы хотите переход на фронт в десять раз дороже. (Многое из этого зависит от того, как копировать дорогой MyType и насколько он является. Если sizeof (MyType) близок к размеру страницы или доступ к MyType в конечном итоге позволяет получить доступ ко многим косвенным образом выделенные объекты, аргумент локальности не будет сохранен.)

With an std::vector, rather than the obvious erase/insert

void
moveToFront( 
    std::vector& list,
    std::vector::iterator element )
{
    MyType tmp( *element );
    std::copy_backwards( list.begin(), std::prev( element ), element );
    *list.begin() = tmp;
}

This will result in less copies than the erase (which copies all of the following elements) insert (which also copies all of the following elements—which means all of the elements, because we are inserting at the beginning) pattern.

9
добавлено
Любая причина не использовать list.front() = tmp вместо * list.begin() = tmp ?
добавлено автор allsey87, источник
Верстка сайтов HTML/CSS/JS/PHP
Верстка сайтов HTML/CSS/JS/PHP
3 439 участник(ов)

Правила группы: напишите !rules в чате. Группа Вк: vk.com/web_structure Freelancer: @web_fl Веб Дизайн: @dev_design Маркетолог: @topmarkening Автор: @M_Boroda

pro.cxx
pro.cxx
3 049 участник(ов)

C/C++ chat 0. Простые вопросы, лабы и о IDE — в чат новичков @supapro 1. Не хамим, не переходим на личности, не вбрасываем утверждения без доказательств 2. No Ads, offtop, flood Объявления о вакансиях и евенты - в лс @AlexFails https://t.me/ProCxx/259155

supapro.cxx
supapro.cxx
1 925 участник(ов)

Чат для тех, кто немного знает C++, простые вопросы по реализации, синтаксису и ide – сюда, а для другого есть: /Главный чат по серьезным вопросам — @ProCxx /Чат по обсуждению всего — @fludpac

CSS — русскоговорящее сообщество
CSS — русскоговорящее сообщество
1 502 участник(ов)

Сообщество любителей CSS Возникли проблемы с CSS? – пиши сюда, обсудим и предложим самое лучшее решение Работа: @css_ru_jobs Правила: https://teletype.in/@css_ru/r1EWtQ2w7 Приходите в наши чаты @javascript_ru и @frontend_ru Флуд: @css_flood

Чат — Типичный Верстальщик
Чат — Типичный Верстальщик
1 080 участник(ов)

Основной канал: @tpverstak Обратная связь: @annblok Все ссылки на соц.сети проекта: http://taplink.cc/tpverstak ПРАВИЛА ЧАТА — https://teletype.in/@annblok/BygPgC3E7

C++ Russia
C++ Russia
384 участник(ов)

Сообщество разработчиков C++ в Telegram.

cxx.Дискуссионная
cxx.Дискуссионная
298 участник(ов)

это не двач, общайтесь вежливо; разговор на почти любые темы; Не согласны с баном? В лс @AlexFails, @ivario

Веб-Технологи: UI/UX, Вёрстка, Фронтенд
Веб-Технологи: UI/UX, Вёрстка, Фронтенд
167 участник(ов)

Всё про веб-дизайн и вёрстку. А также: HTML, CSS, флекс и бутстрапы, шаблонизаторы, препроцессоры, методологии, аглифаеры, улучшаторы и обфускаторы. Обсуждаем темы юзабилити, устраиваем А/В тесты лендингов, и проводим аудит.

DTP :: @DTPublish
DTP :: @DTPublish
147 участник(ов)

Обсуждаемые темы: полиграфия, препресс, верстка, дизайн, иллюстрации, скрипты, плагины. Канал - @DTPublishing

C++ для маленьких и тупых
C++ для маленьких и тупых
105 участник(ов)

Лоу левел (по среднему IQ участников) чатик ExtremeCode @extremecode Флудилка @extremecode_rest

css_jobs
css_jobs
26 участник(ов)

Чат для вопросов по css и html: @css_ru Флуд: @css_flood Канал с вакансиями и резюме: @css_jobs_feed

css_флуд
css_флуд
10 участник(ов)