Menu
Coddy logo textTech

Lista jednokierunkowa (linked list)

Ostatnia aktualizacja

Lista jednokierunkowa przechowuje sekwencję jako łańcuch węzłów, w którym każdy węzeł zawiera wartość i wskaźnik na następny węzeł. W przeciwieństwie do tablicy węzły nie leżą w pamięci obok siebie: aby przejść listę, podążasz za wskaźnikami next. Kliknij odtwarzanie powyżej i zobacz, jak węzły są dołączane na początku i na końcu, jak wartość jest wyszukiwana przez przejście łańcucha i jak węzeł jest usuwany przez przepięcie wskaźnika.

Wstawianie lub usuwanie na początku kosztuje O(1), bo wystarczy przestawić głowę. Dotarcie do pozycji w środku lub na końcu kosztuje O(n), bo najpierw trzeba tam dojść. Ten kompromis, czyli tanie końce i brak dostępu swobodnego, odróżnia listę jednokierunkową od tablicy.

Złożoność czasowa

OperacjaZłożonośćUwagi
Wstawianie na początekO(1)Przestawienie głowy
Wstawianie na koniecO(n)Najpierw przejście do końca (O(1) ze wskaźnikiem tail)
WyszukiwanieO(n)Podążanie za wskaźnikami next
Usuwanie głowyO(1)Przestawienie głowy za usuwany węzeł
Dostęp po indeksieO(n)Brak dostępu swobodnego

Lista jednokierunkowa a tablica

AspektLista jednokierunkowaTablica
PamięćRozproszone węzły + wskaźnikiCiągły blok
Dostęp swobodnyO(n)O(1)
Wstawianie/usuwanie na początkuO(1)O(n) (przesunięcie)
Współpraca z pamięcią podręcznąSłabaDobra

Przykład krok po kroku

Budowanie listy [10, 20], dodanie 5 na początek, a potem usunięcie 20:

KrokStrukturaDziałanie
Starthead -> nullPusta lista
Wstaw 10 na początekhead -> 10 -> nullPrzestaw głowę na nowy węzeł, którego next to stara głowa (null)
Wstaw 20 na koniechead -> 10 -> 20 -> nullPrzejdź do węzła 10, ustaw jego next na nowy węzeł 20
Wstaw 5 na początekhead -> 5 -> 10 -> 20 -> nullNowy węzeł 5 wskazuje na starą głowę 10; głowa wskazuje teraz na 5
Usuń 20head -> 5 -> 10 -> nullPrzejdź do 10, zmień jego next z 20 na null; węzeł 20 zostaje odłączony

Kiedy używać listy jednokierunkowej

Używaj, gdyUnikaj, gdy
Znacznie częściej wstawiasz lub usuwasz na końcach (lub przy trzymanym węźle), niż odwołujesz się po indeksiePotrzebujesz szybkiego dostępu swobodnego po pozycji (indeksowanie w O(1))
Rozmiar często się zmienia i nie chcesz kosztów zmiany rozmiaru ani kopiowaniaWykonujesz ciasne pętle, w których o wydajności decyduje lokalność pamięci podręcznej
Budujesz kolejkę, stos lub listę sąsiedztwaPamięci jest mało: każdy węzeł płaci za dodatkowy wskaźnik na element
Potrzebujesz stabilnych referencji do węzłów, które przetrwają wstawienia w innych miejscachGłównie czytasz dane i rzadko je zmieniasz, więc tablica jest prostsza i szybsza

Linked List: kod

Przejrzysta, gotowa do uruchomienia implementacja algorytmu Linked List w językach: Python, JavaScript, Java, C++, C. Wybierz język, skopiuj kod albo otwórz go od razu w edytorze online Coddy.

Linked List: kod (Python)

Python
1class Node:2    def __init__(self, value):3        self.value = value4        self.next = None5
6
7class LinkedList:8    def __init__(self):9        self.head = None10
11    def append(self, value):12        node = Node(value)13        if self.head is None:14            self.head = node15            return16        current = self.head17        while current.next:18            current = current.next19        current.next = node20
21    def find(self, value):22        current = self.head23        while current:24            if current.value == value:25                return True26            current = current.next27        return False28
29    def delete(self, value):30        # Re-link the previous node around the match31        current, prev = self.head, None32        while current:33            if current.value == value:34                if prev is None:35                    self.head = current.next36                else:37                    prev.next = current.next38                return True39            prev, current = current, current.next40        return False41
42    def __str__(self):43        values, current = [], self.head44        while current:45            values.append(str(current.value))46            current = current.next47        return " -> ".join(values) + " -> None"48
49
50lst = LinkedList()51for value in [3, 7, 1, 9]:52    lst.append(value)53
54print("List:    ", lst)55print("find(7): ", lst.find(7))56lst.delete(1)57print("After delete(1):", lst)
Uruchom ten kod w edytorze Python online

Lista jednokierunkowa: najczęstsze pytania

Czym różni się lista jednokierunkowa od tablicy?
Tablica przechowuje elementy w jednym ciągłym bloku, co daje dostęp swobodny w O(1), ale wstawianie i usuwanie w O(n) z przesuwaniem elementów. Lista jednokierunkowa przechowuje węzły w dowolnych miejscach pamięci połączone wskaźnikami, co daje wstawianie i usuwanie w O(1) w znanej pozycji, ale dostęp w O(n), bo trzeba przejść łańcuch.
Kiedy używać listy jednokierunkowej?
Listy jednokierunkowe sprawdzają się, gdy często wstawiasz lub usuwasz elementy na końcach (albo przy węźle, do którego masz już referencję) i rzadko potrzebujesz dostępu swobodnego, na przykład w kolejkach, stosach, listach sąsiedztwa i pamięciach podręcznych LRU. Jeśli potrzebujesz szybkiego dostępu po indeksie, zwykle lepsza jest tablica lub tablica dynamiczna.
Jaka jest złożoność czasowa listy jednokierunkowej?
Wstawianie lub usuwanie na początku kosztuje O(1). Wyszukiwanie albo dotarcie do końca bez wskaźnika tail kosztuje O(n), bo podążasz za wskaźnikami next jeden po drugim. Nie ma dostępu swobodnego w O(1): odwołanie po indeksie w liście jednokierunkowej kosztuje O(n).
Czym różni się lista jednokierunkowa od dwukierunkowej?
Lista jednokierunkowa przechowuje w każdym węźle tylko wskaźnik next, więc można iść w jednym kierunku, a usunięcie węzła wymaga referencji do jego poprzednika. Lista dwukierunkowa dodaje wskaźnik prev, który umożliwia przechodzenie wstecz i usuwanie trzymanego węzła w O(1), kosztem dodatkowego wskaźnika na węzeł i większej pracy przy każdym wstawieniu i usunięciu.
Dlaczego wstawianie na koniec kosztuje O(n), skoro wstawianie na początek kosztuje O(1)?
Wstawianie na początek tylko przepina wskaźnik head, co jest stałą pracą. Dotarcie do końca oznacza podążanie za wskaźnikami next od głowy aż do samego końca, co kosztuje O(n). Osobny wskaźnik tail sprawia, że wstawianie na koniec też kosztuje O(1), dlatego w praktyce listy często śledzą oba końce.
Czy listy jednokierunkowe mają wstawianie w O(1) wszędzie?
Nie, to częste nieporozumienie. Samo wstawienie kosztuje O(1) tylko wtedy, gdy masz już wskaźnik na węzeł, za którym wstawiasz. Znalezienie tej pozycji po wartości lub indeksie nadal kosztuje O(n), bo trzeba do niej dojść po łańcuchu.
Ilustracja języków programowania w Coddy

Opanuj algorytmy z Coddy

ZACZNIJ