Czy list:: size() Jest naprawdę O (n)?

Ostatnio zauważyłem, że niektórzy ludzie wspominają, że std::list::size() ma złożoność liniową.
Według niektórych sources , w rzeczywistości jest to zależne od implementacji, ponieważ standard nie mówi, jaka musi być złożoność.
Komentarz w tym wpisie na blogu mówi:

Właściwie, to zależy od tego, który STL ty używasz. Microsoft Visual Studio V6 implementuje size () jako {return (_Size); } natomiast gcc (przynajmniej w wersjach 3.3.2 i 4.1.0) czynią to jako { return std:: distance (begin (), end ());} The pierwszy ma stałą prędkość, drugi ma prędkość O (N)

  1. więc zgaduję, że dla VC++ tłum size() ma stałą złożoność jako Dinkumware prawdopodobnie nie zmieni tego faktu od VC6. Jestem tam?
  2. jak wygląda obecnie w gcc? Jeśli to naprawdę O (n), to dlaczego deweloperzy decydują się na to?
Author: wprl, 2008-10-23

7 answers

Pre-C++11 odpowiedź

Masz rację, że standard nie określa złożoności list::size () - zaleca jednak, aby "miał stałą złożoność" (uwaga A w tabeli 65).

Oto ciekawy artykuł Howarda Hinnanta , który wyjaśnia, dlaczego niektórzy ludzie myślą, że list::size() powinna mieć złożoność O(N) (zasadniczo dlatego, że wierzą, że O(1) list::size() sprawia, że list::splice() ma złożoność O(N)) i dlaczego O(1) lista:: size() jest dobrym pomysłem (zdaniem autora):

Myślę, że główne punkty w artykule to:

  • jest kilka sytuacji, w których utrzymanie wewnętrznej liczby tak, że list::size() może być O(1) powoduje, że operacja splice staje się liniowa
  • jest prawdopodobnie wiele innych sytuacji, w których ktoś może być nieświadomy negatywnych skutków, które mogą się wydarzyć, ponieważ nazywają O (N) size() (taki jak jego jeden przykład, gdzie list::size() jest wywoływany podczas trzymania zamka).
  • że zamiast pozwolić size() BYĆ O(N), w interesie "najmniejszego zaskoczenia", norma powinna wymagać od każdego kontenera implementującego size() wdrożenia go w sposób O(1). Jeśli kontener nie może tego zrobić, nie powinien w ogóle implementować size(). W takim przypadku użytkownik kontenera zostanie poinformowany, że {[1] } jest niedostępny, a jeśli nadal chce lub musi uzyskać liczbę elementów w kontener mogą nadal używać container::distance( begin(), end()), aby uzyskać tę wartość - ale będą w pełni świadomi, że jest to operacja O(N).
Myślę, że Zgadzam się z większością jego rozumowań. Nie podoba mi się jednak jego zaproponowany dodatek do przeciążeń splice(). Przejście W n, które musi być równe distance( first, last), aby uzyskać poprawne zachowanie, wydaje się receptą na trudne do zdiagnozowania błędy.

Nie jestem pewien, co należy lub co można zrobić, ponieważ każda zmiana miałaby znaczący wpływ na istniejący kod. Ale w obecnym stanie rzeczy, myślę, że istniejący kod jest już dotknięty-zachowanie może się znacznie różnić od jednej implementacji do drugiej dla czegoś, co powinno być dobrze zdefiniowane. Być może komentarz onebyone o rozmiarze "buforowanym" i oznaczonym jako znany/nieznany może działać dobrze - otrzymasz amortyzowane zachowanie O(1) - jedyny raz, gdy otrzymasz zachowanie O(N), jest wtedy, gdy lista jest modyfikowana przez niektóre operacje splice (). Fajne w tym jest to, że może być zrobione dzisiaj przez implementatorów bez zmiany standardu (chyba, że czegoś mi brakuje).

Z tego co wiem, C++0x niczego nie zmienia w tej dziedzinie.

 50
Author: Michael Burr,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-09-14 16:43:26

W C++11 wymagane jest, aby dla dowolnego standardowego kontenera operacja .size() musi być zakończona w "stałej" złożoności (O(1)). (Tabela 96-wymagania dotyczące kontenerów). Poprzednio w C++03 .size() powinna mieć stałą złożoność, ale nie jest wymagana (zobacz czy std::string size() jest operacją O(1)?).

Zmiana standardu jest wprowadzana przez n2923: określanie złożoności size () (Wersja 1) .

[[7]}jednak realizacja .size() w libstdc++ nadal używa algorytmu O(N) w gcc do 4.8:
  /**  Returns the number of elements in the %list.  */
  size_type
  size() const _GLIBCXX_NOEXCEPT
  { return std::distance(begin(), end()); }

Zobacz także dlaczego std:: list jest większy w c++11? dla szczegółów, dlaczego jest tak utrzymywany.

Aktualizacja: std::list::size() jest poprawnie O (1) podczas używania gcc 5.0 w trybie C++11 (lub nowszym).


Przy okazji, .size() w libc++ jest poprawnie O (1):

_LIBCPP_INLINE_VISIBILITY
size_type size() const _NOEXCEPT     {return base::__sz();}

...

__compressed_pair<size_type, __node_allocator> __size_alloc_;

_LIBCPP_INLINE_VISIBILITY
const size_type& __sz() const _NOEXCEPT
    {return __size_alloc_.first();}
 61
Author: kennytm,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 11:47:32

Musiałem wcześniej zajrzeć do listy GCC 3.4:: size, więc mogę powiedzieć tak:

  1. używa STD:: distance (head, tail)
  2. STD::distance ma dwie implementacje: dla typów, które spełniają RandomAccessIterator, używa "tail-head", a dla typów, które spełniają jedynie InputIterator, używa algorytmu O (n) opierającego się na "iterator++", licząc, aż trafi w dany ogon.
  3. std:: lista nie satsyfikuje RandomAccessIterator, więc rozmiar to O (n).

Co do "Dlaczego", mogę powiedzmy tylko, że STD:: list jest odpowiednie dla problemów, które wymagają dostępu sekwencyjnego. Przechowywanie rozmiaru jako zmiennej klasy wprowadzi narzut na każdym insert, delete, itp., a te marnotrawstwo to wielkie Nie-Nie zgodnie z intencją STL. Jeśli naprawdę potrzebujesz stałej wielkości (), użyj STD:: deque.

 14
Author: introp,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-10-23 17:25:18

Osobiście nie widzę problemu z O(N) jako jedynym powodem, dla którego rozmiar może być O(N). nie płacisz za to, czego nie używasz to ważne motto C++. W takim przypadku utrzymanie rozmiaru listy wymaga dodatkowego zwiększania / zmniejszania przy każdym wstawianiu/kasowaniu, niezależnie od tego, czy sprawdzasz rozmiar listy, czy nie. Jest to mały stały nad głową, ale nadal ważne do rozważenia.

Sprawdzanie rozmiaru listy jest rzadko potrzebne. Iteracja od początku do końca bez dbanie o całkowity rozmiar jest nieskończenie powszechniejsze.

 11
Author: Greg Rogers,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-10-23 13:21:39

Udałbym się do Źródła . Strona STL SGI mówi, że dozwolone jest posiadanie złożoności liniowej. Uważam, że wytyczne projektowe, którymi się kierowali, polegały na tym, aby implementacja listy była jak najbardziej ogólna, a tym samym aby umożliwić większą elastyczność w korzystaniu z list.

 4
Author: Yuval F,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-10-23 08:18:07

This bug report: [C++0x] STD::list::size complexity, przechwytuje w niewyobrażalny sposób fakt, że implementacja w GCC 4.x jest czasem liniowym i jak przejście na stały czas dla C++11 było powolne (dostępne w wersji 5.0) ze względu na problemy z kompatybilnością ABI.

Strona podręcznika dla serii GCC 4.9 nadal zawiera następujące zastrzeżenie:

Wsparcie dla C++11 jest nadal eksperymentalne, a w przyszłości mogą ulec zmianie w sposób niezgodny wydania.


Ten sam raport o błędzie znajduje się tutaj: czy std::list:: size powinno mieć stałą złożoność w C++11?

 1
Author: nobar,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 12:26:34

Jeśli poprawnie używasz list, prawdopodobnie nie zauważysz żadnej różnicy.

Listy są dobre z dużymi strukturami danych, które chcesz zmienić bez kopiowania, dla danych, które chcesz zachować ważne wskaźniki po wstawieniu.

W pierwszym przypadku nie robi to różnicy, w drugim wolałbym starą (mniejszą) implementację size ().

W każdym razie std to bardziej poprawność i standardowe zachowanie i "przyjazność dla użytkownika" niż szybkość raw.

 0
Author: Luke Givens,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-18 14:09:34