Ежедневный бит (е) C ++ # 105, Распространенная проблема на собеседовании: удаление n-го элемента из конца односвязного списка

Сегодня мы рассмотрим распространенную задачу интервью C++: удаление n-го элемента из конца односвязного списка.

Учитывая односвязный список, удалите n-й элемент из конца списка.

Вы должны сделать это за один проход и только с постоянной дополнительной памятью.

Приведенная выше иллюстрация предназначена для n==2, т. е. для входных данных {1,2,3,4,5} и n==2 результат равен {1,2,3,5}.

Прежде чем вы продолжите чтение, я рекомендую вам попробовать решить ее самостоятельно. Вот ссылка на Compiler Explorer с парой тестов: https://compiler-explorer.com/z/bMdMnxbbn.

Решение

Сложность этой задачи в том, что она решается за один проход и с постоянной дополнительной памятью.

С O(n*n) мы могли бы многократно сканировать n элементов вперед. Однако, если подумать, это очень расточительно. Если мы знаем предыдущую пару элементов, которые находятся на расстоянии n друг от друга, мы можем перейти к следующей паре, просто переместив оба.

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

#include <forward_list>

void remove_nth_from_end(std::forward_list<int>& head, int n) {
   // start with tail one ahead
    auto node = head.before_begin();
    auto tail = head.begin();
   // advance it so it is n elements ahead
    for (int i = 1; i < n && tail != head.end(); ++i)
        ++tail;

   // if there aren't enough elements in the list
    if (tail == head.end()) return;

   // advance both iterators together
    while (std::next(tail) != head.end()) {
        ++node;
        ++tail;
    }

   // remove the n-th element
    head.erase_after(node);
}

Откройте решение в Compiler Explorer.