Dlaczego funkcja odwrotna dla std::list
klasy w standardowej bibliotece C ++ ma liniowe środowisko wykonawcze? Sądzę, że dla podwójnie powiązanych list funkcją odwrotną powinna być O (1).
Odwrócenie podwójnie połączonej listy powinno po prostu obejmować zmianę wskaźników głowy i ogona.
c++
c++11
stl
linked-list
Ciekawy
źródło
źródło
Reverse
funkcja zostanie zaimplementowana w O (1)?Odpowiedzi:
Hipotetycznie
reverse
mógł to być O (1) . Tam (ponownie hipotetycznie) mógł istnieć element listy boolowskiej wskazujący, czy kierunek połączonej listy jest obecnie taki sam, czy przeciwny, niż pierwotny kierunek, w którym lista została utworzona.Niestety zmniejszyłoby to wydajność praktycznie każdej innej operacji (choć bez zmiany asymptotycznego środowiska wykonawczego). W każdej operacji należy skonsultować się z wartością logiczną, aby zastanowić się, czy zastosować wskaźnik „następny”, czy „poprzedni” linku.
Ponieważ przypuszczalnie uznano to za stosunkowo rzadką operację, standard (który nie dyktuje implementacji, tylko złożoność) określa, że złożoność może być liniowa. Pozwala to „następnym” wskaźnikom zawsze jednoznacznie oznaczać ten sam kierunek, przyspieszając operacje w typowych przypadkach.
źródło
reverse
zeO(1)
złożoności bez wpływu na big-O jakiejkolwiek innej operacji , za pomocą tego logiczną flagi podstęp. Ale w praktyce dodatkowy oddział w każdej operacji jest kosztowny, nawet jeśli technicznie jest to O (1). Natomiast nie można utworzyć struktury listy, w którejsort
jest O (1), a wszystkie pozostałe operacje są takie same. Chodzi o to, że najwyraźniej możesz uzyskać zwrotO(1)
za darmo, jeśli zależy ci tylko na dużym O, więc dlaczego tego nie zrobili.std::uintptr_t
. Potem możesz je xor.std::uintptr_t
, możesz rzutować nachar
tablicę, a następnie XOR komponentów. Byłby wolniejszy, ale w 100% przenośny. Prawdopodobnie możesz dokonać wyboru między tymi dwiema implementacjami i użyć drugiej jako rezerwowej, jeśli jejuintptr_t
brakuje. Niektóre, jeśli jest to opisane w tej odpowiedzi: stackoverflow.com/questions/14243971/…Może to być O (1), jeśli lista przechowuje flagę, która umożliwia zamianę znaczenia wskaźników „
prev
” i „next
” każdego węzła. Jeśli odwrócenie listy byłoby częstą operacją, takie dodanie może być w rzeczywistości przydatne i nie znam żadnego powodu, dla którego wdrożenie byłoby zabronione przez obecny standard. Posiadanie takiej flagi spowodowałoby jednak, że zwykłe przejście na listę byłoby droższe (choćby o stały czynnik), ponieważ zamiastw
operator++
iteratorze listy, dostanieszco nie jest łatwe do dodania. Biorąc pod uwagę, że listy są zwykle przeszukiwane znacznie częściej niż są odwrócone, normalne byłoby wymuszanie tej techniki. Dlatego operacja odwrotna może mieć złożoność liniową. Należy jednak pamiętać, że t ∈ O (1) ⇒ t ∈ O ( n ), więc, jak wspomniano wcześniej, technicznie możliwe byłoby wdrożenie „optymalizacji”.
Jeśli pochodzisz z języka Java lub podobnego, możesz zastanawiać się, dlaczego iterator musi za każdym razem sprawdzać flagę. Czy nie moglibyśmy zamiast tego mieć dwa różne typy iteratorów, oba wywodzące się ze wspólnego typu podstawowego, i mieć
std::list::begin
istd::list::rbegin
zwracać polimorficznie odpowiedni iterator? Chociaż jest to możliwe, pogorszyłoby to wszystko, ponieważ przyspieszenie iteratora byłoby teraz wywołaniem funkcji pośredniej (trudnej do wstawienia). W Javie i tak płacisz tę cenę rutynowo, ale z drugiej strony jest to jeden z powodów, dla których wiele osób korzysta z C ++, gdy wydajność jest krytyczna.Jak zauważył Benjamin Lindley w komentarzach, ponieważ
reverse
nie wolno unieważniać iteratorów, jedynym podejściem dozwolonym przez standard wydaje się być przechowywanie wskaźnika z powrotem do listy wewnątrz iteratora, co powoduje podwójnie pośredni dostęp do pamięci.źródło
std::list::reverse
nie unieważnia iteratorów.next
iprev
wskaźniki w tablicy i przechowuj kierunek jako0
lub1
. Aby wykonać iterację do przodu, należy wykonać czynnościpointers[direction]
iteracyjne w odwrotnej kolejnościpointers[1-direction]
(lub odwrotnie). To wciąż dodaje odrobinę narzutu, ale prawdopodobnie mniej niż gałąź.swap()
jest określony jako stały czas i nie unieważnia żadnych iteratorów.Z pewnością, ponieważ wszystkie kontenery obsługujące dwukierunkowe iteratory mają pojęcie rbegin () i rend (), to pytanie jest dyskusyjne?
Budowanie serwera proxy odwracającego iteratory i uzyskiwanie do niego dostępu do kontenera jest banalne.
Ten brak działania jest rzeczywiście O (1).
Jak na przykład:
oczekiwany wynik:
Biorąc to pod uwagę, wydaje mi się, że komitet normalizacyjny nie poświęcił czasu na zlecenie O (1) odwrotnego zamawiania kontenera, ponieważ nie jest to konieczne, a biblioteka standardowa jest w dużej mierze zbudowana na zasadzie mandowania tylko tego, co jest ściśle konieczne, podczas gdy unikanie powielania.
Tylko mój 2c.
źródło
Ponieważ musi przejść przez każdy węzeł (
n
łącznie) i zaktualizować ich dane (krok aktualizacji jest rzeczywiścieO(1)
). To sprawia, że cała operacjaO(n*1) = O(n)
.źródło
Zamienia również poprzedni i następny wskaźnik dla każdego węzła. Właśnie dlatego bierze Linear. Chociaż można to zrobić w O (1), jeśli funkcja korzystająca z tego LL pobiera również informacje o LL jako dane wejściowe, takie jak to, czy uzyskuje dostęp normalnie, czy odwrotnie.
źródło
Tylko wyjaśnienie algorytmu. Wyobraź sobie, że masz tablicę z elementami, a następnie musisz ją odwrócić. Podstawową ideą jest iteracja każdego elementu zmieniającego element z pierwszej pozycji do ostatniej pozycji, element z drugiej pozycji do pozycji przedostatniej i tak dalej. Kiedy osiągniesz środek tablicy, zmienisz wszystkie elementy, a więc w (n / 2) iteracjach, co jest uważane za O (n).
źródło
Jest to O (n) po prostu dlatego, że musi skopiować listę w odwrotnej kolejności. Każda pojedyncza operacja na elemencie to O (1), ale n jest ich na całej liście.
Oczywiście istnieją pewne operacje wykonywane w czasie stałym związane z ustawieniem miejsca dla nowej listy, późniejszą zmianą wskaźników itp. Notacja O nie uwzględnia indywidualnych stałych, jeśli uwzględni się czynnik n rzędu pierwszego.
źródło