Разминка для мозгов: циклический сдвиг контейнера.

Задача: циклически сдвинуть последовательный контейнер (некий аналог std::rotate),
например, std::vector, или обычный массив...

1) Для новичков.

a) Интерфейс функции rotate можно сделать любым,
который будет удобен, например,

void rotate(int*, std::size_t count, std::size_t shift);

или как у std::rotate — основанный на итераторах, сути дела не меняет.
Данные, поступающие от вызывающей стороны гарантированно корректны.

2) Задача для чуть более сведущего новичка.

a) Это должен быть шаблон функции
(элементы гарантировано могут копироваться).
b) Вся работа должна происходить с одним контейнером,
т.е. резервировать себе динамическую память
или использовать другие контейнеры
для копирования туда элементов — запрещено.
Использовать дополнительную переменную
на стеке для обмена элементов разрешено.
Также можно использовать std::swap, std::iter_swap и др.
Т.е. упор на реализацию без временного хранилища элементов.

3) Еще чуть усложним.

a) Алгоритм должен работать максимум за линейное время.
b) Возможность сдвигать не только контейнеры,
предоставляющие итераторы произвольного доступа,
но, например, и std::list и std::forward_list,
т.е. функция должна уметь работать
и с однонаправленными итераторами.

Что еще дополнить или что не понятно?

Вот такое решение:

template < class T >
void rotate_t(T *arr, std::size_t count, std::size_t shift) {
    shift = shift % count;                  // эквивалентный сдвиг
    if (shift == 0) {                       // тривиальный случай
        return;
    }
    T tmp;                                  // для временного значения
    size_t src = 0;                         // индекс элемента-источника
    size_t dest;                            // индекс элемента-приемника
    size_t swap_counter = 0;                // счетчик переставленных элементов
    do {
        tmp = arr[src];                     // копируем элемент-источник
        dest = src;
        do {                                // переставлять по цепочке, пока есть возможность
            dest = (dest + shift) % count;  // индекс очередного элемента-приемника
            swap(arr[dest], tmp);           // теперь в tmp следующий элемент из цепочки
            swap_counter++;
        } while (dest != src);              // продолжать, пока не достигли исходного индекса
        src++;                              // перейти к следующему элементу
    } while (swap_counter < count);         // продолжать, пока не все элементы переставлены
}

Пункты 1) и 2) — полностью.
Пункт 3а — вроде должен работать за линейное время как я понимаю.

Пункт 3b — не понял.
Там наверное и интерфейс функции будет другим.
И я что-то воткнуть ни как не могу, как должен выглядеть шаблон, который описывает параметр-тип контейнер с параметром-типом, и что бы еще этот тип был доступен в функции. Например, что бы при вызове

vector<int> vec;
rotate<vector<int>>(vec, shift);

в теле шаблонной функции были доступны и тип 'vector', как тип-контейнер, и тип 'int' как тип-элемент этого типа-контейнера. А еще лучше, что бы шаблон инстанцировался по типу первого параметра. Возможно?

А такой вариант вызова:

int array[SIZE];
rotate<int*>(array, shift);

получится совместить с предыдущим?

У меня с шаблонами пока напряги :(


Кстати, насчет уровня заданий. Меня тут упрекнули, что я заумные решения на вопросы начинающих пишу. Так вот твои задания 2), 3) — явно не для начинающих, тусующихся на этом сайте. Сам видишь, сколько с 17 января было желающих размять мозги.

А где мой пост, который ушел на модерацию?

template<typename ForwardIterator>
void rotate(ForwardIterator begin, ForwardIterator first, ForwardIterator end)
{
    while (begin != first && first != end) {
        ForwardIterator beginCopy = first;
        for (; begin != first && beginCopy != end; ++begin, ++beginCopy) {
            std::iter_swap(begin, beginCopy);
        }
        if (begin == first) {
            first = beginCopy;
        }
    }
}

http://rextester.com/QTJP34905

Меня тут упрекнули, что я заумные решения на вопросы начинающих пишу.

Не обращайте внимания. Решайте для себя, а не для них.
Забейте на всех, занимайтесь собой, делайте то, что Вам интересно.
А все эти «НИ ТАК НАДА» идут лесом, вместе с лентяями-студентами.
Только в таком случае Вы принесете пользу себе и окружающим.

P.S. Но к конструктивной критике прислушивайтесь, к той,
которая призвана улучшить Ваше решение или подход.

Внимание! Это довольно старый топик, посты в него не попадут в новые, и их никто не увидит. Пишите пост, если хотите просто дополнить топик, а чтобы задать новый вопрос — начните новый.

Ответить

Вы можете использовать разметку markdown для оформления комментариев и постов. Используйте функцию предпросмотра для проверки корректности разметки.

Пожалуйста, оформляйте исходный код в соответствии с правилами разметки. Для того, чтобы вставить код в комментарий, скопируйте его в текстовое поле ниже, после чего выделите то, что скопировали и нажмите кнопку «код» в панели инструментов. Иначе ваш код может принять нечитаемый вид.

Либо производите оформление кода вручную, следующим образом:

``` #include <iostream> using namespace std; int main() { // ... } ```

Предпросмотр сообщения

Ваше сообщение пусто.