- PVSM.RU - https://www.pvsm.ru -
Односвзязный список — это фундаментальная структура данных, о которой все знают и повсеместно используют. Все знают почему и в каких случаях он эффективнее вектора, какие операции имеют линейную сложность, а какие — константную…
Хотя постойте, знаете ли вы какова соложность функции size ()?
«Конечно же я знаю — О(1)!», ответят многие из вас, «Что может быть проще?»
size_type size() const
{
return _size;
}
Тривиально, эффективно и безопасно, не так ли?
Я бы реализовал эту функцию именно так, большинство из вас сделали бы тоже самое.
Однако, те, кто писал реализацию GNU STL, другого мнения на этот счет.
Вот так выглядит реализация этой функции в gcc 4.x:
/** Returns the number of elements in the %list. */
size_type
size() const
{ return std::distance(begin(), end()); }
Если не верите — пойдите проверьте в своем дистрибутиве.
Что мы здесь видим? Чтобы выполнисть такую тривиальную операцию, как получение размера, наш список будет выполнять полный проход с подсчетом!
for (std::list <int>::const_iterator I = my_list.begin (); I != my_list.end (); ++I)
{
if (*I > my_list.size ())
{
std::cout << "Haha! "<< *I << std::endl;
}
}
Вместо, казалось бы очевидного, O(N), мы получим здесь O(N^2). Хорошо если в списке 100 элементов. А что если 1 000 000?
std::list <int> my_list;
// Thread 1
void thread1_proc ()
{
while (!stop)
{
std::cout << "List size: " << my_list.size () << std::endl;
}
}
// Thread 2
void thread2_proc ()
{
while (!stop)
{
int n = rand ()
my_list.push_back (n);
if (n % 2)
my_list.pop_front ();
}
}
Имеем серьезный риск получить крэш в первом потоке.
Оказывается, отсутствие необходимости поддерживать внутреннюю переменную _size позволяет реализовать splice за O(1), иначе было бы O(N) для худшего случая.
splice — это операция переноса последовательных элементов из одного списка в другой. Не имея необходимости подсчитывать новые размеры списков, нам достаточно «переключить» указатели с одних узлов на другие, т.е. за константное время.
Именя же внутреннюю переменную _size, нам бы пришлось посчитать сколько элементов мы переносим и соответсвенно обновить ее в обоих списках.
Вот такая вот причина. Кстати, как часто вы пользуетесь это операцией? А всеми вышеперечисленными?
Вобщем будте осторожнее. И еще имейте в виду, что в других реализациях STL переменная _size есть и size() соответственно работает за O(1). Так что будьте дважды осторожны, если вы собираете свой проэкт с разными реализациями STL на разных платформах.
На сем раскланиваюсь. Простите за ботанический пост в пятницу.
Автор: Gunnar
Сайт-источник PVSM.RU: https://www.pvsm.ru
Путь до страницы источника: https://www.pvsm.ru/c-3/12933
Нажмите здесь для печати.