Implementacja tabeli mieszającej w Pythonie przy użyciu oddzielnego łączenia łańcuchowego

Implementacja tabeli mieszającej w Pythonie przy użyciu oddzielnego łączenia łańcuchowego

A tablica mieszająca to struktura danych, która pozwala na szybkie wstawianie, usuwanie i odzyskiwanie danych. Działa poprzez użycie funkcji skrótu do mapowania klucza na indeks w tablicy. W tym artykule zaimplementujemy tabelę mieszającą w Pythonie, używając oddzielnego łańcucha do obsługi kolizji.

Składniki hashingu

Składniki hashowania

Oddzielne łączenie łańcuchowe to technika stosowana do obsługi kolizji w tabeli mieszającej. Kiedy dwa lub więcej kluczy jest przypisanych do tego samego indeksu w tablicy, przechowujemy je na połączonej liście pod tym indeksem. Dzięki temu możemy przechowywać wiele wartości w tym samym indeksie i nadal móc je odzyskać za pomocą ich klucza.

Sposób implementacji tabeli mieszającej przy użyciu oddzielnego łączenia łańcuchowego

Sposób implementacji tabeli mieszającej przy użyciu oddzielnego łączenia łańcuchowego:

Utwórz dwie klasy: „ Węzeł ' I ' Tabela Hash '.

Węzeł Klasa „będzie reprezentować węzeł na połączonej liście. Każdy węzeł będzie zawierał parę klucz-wartość, a także wskaźnik do następnego węzła na liście.

Python3




class> Node:> > def> __init__(> self> , key, value):> > self> .key> => key> > self> .value> => value> > self> .> next> => None>

Klasa „HashTable” będzie zawierać tablicę, w której będą przechowywane połączone listy, a także metody wstawiania, pobierania i usuwania danych z tabeli mieszającej.

Python3




class> HashTable:> > def> __init__(> self> , capacity):> > self> .capacity> => capacity> > self> .size> => 0> > self> .table> => [> None> ]> *> capacity>

__gorący__ Metoda inicjuje tablicę mieszającą o podanej pojemności. Ustawia „ pojemność ' I ' rozmiar ' i inicjuje tablicę na 'Brak'.

Następną metodą jest „ _haszysz ' metoda. Ta metoda pobiera klucz i zwraca indeks w tablicy, w której powinna być przechowywana para klucz-wartość. Użyjemy wbudowanej funkcji skrótu Pythona do zaszyfrowania klucza, a następnie użyjemy operatora modulo, aby uzyskać indeks w tablicy.

Python3




def> _hash(> self> , key):> > return> hash> (key)> %> self> .capacity>

The 'wstawić' metoda wstawi parę klucz-wartość do tabeli mieszającej. Pobiera indeks, w którym para powinna być przechowywana, za pomocą „ _haszysz ' metoda. Jeśli w tym indeksie nie ma połączonej listy, tworzy nowy węzeł z parą klucz-wartość i ustawia go jako nagłówek listy. Jeśli w tym indeksie znajduje się lista połączona, iteruj po liście, aż znajdziesz ostatni węzeł lub klucz już istnieje, a następnie zaktualizuj wartość, jeśli klucz już istnieje. Jeśli znajdzie klucz, aktualizuje wartość. Jeżeli nie znajdzie klucza, tworzy nowy węzeł i dodaje go na początek listy.

Python3




def> insert(> self> , key, value):> > index> => self> ._hash(key)> > if> self> .table[index]> is> None> :> > self> .table[index]> => Node(key, value)> > self> .size> +> => 1> > else> :> > current> => self> .table[index]> > while> current:> > if> current.key> => => key:> > current.value> => value> > return> > current> => current.> next> > new_node> => Node(key, value)> > new_node.> next> => self> .table[index]> > self> .table[index]> => new_node> > self> .size> +> => 1>

The szukaj metoda pobiera wartość związaną z danym kluczem. Najpierw pobiera indeks, w którym powinna być przechowywana para klucz-wartość, za pomocą metody _haszysz metoda. Następnie przeszukuje połączoną listę pod tym indeksem w poszukiwaniu klucza. Jeśli znajdzie klucz, zwraca powiązaną wartość. Jeśli nie znajdzie klucza, zgłasza a Błąd klucza .

Python3




def> search(> self> , key):> > index> => self> ._hash(key)> > current> => self> .table[index]> > while> current:> > if> current.key> => => key:> > return> current.value> > current> => current.> next> > raise> KeyError(key)>

The 'usunąć' Metoda usuwa parę klucz-wartość z tablicy mieszającej. Najpierw pobiera indeks, w którym para powinna być przechowywana, za pomocą ` _haszysz ` metoda. Następnie przeszukuje połączoną listę pod tym indeksem w poszukiwaniu klucza. Jeśli znajdzie klucz, usuwa węzeł z listy. Jeśli nie znajdzie klucza, wywołuje ` Błąd klucza `.

Python3




def> remove(> self> , key):> > index> => self> ._hash(key)> > > previous> => None> > current> => self> .table[index]> > > while> current:> > if> current.key> => => key:> > if> previous:> > previous.> next> => current.> next> > else> :> > self> .table[index]> => current.> next> > self> .size> -> => 1> > return> > previous> => current> > current> => current.> next> > > raise> KeyError(key)>

„__str__” metoda zwracająca ciąg znaków reprezentujący tabelę skrótów.

Python3




def> __str__(> self> ):> > elements> => []> > for> i> in> range> (> self> .capacity):> > current> => self> .table[i]> > while> current:> > elements.append((current.key, current.value))> > current> => current.> next> > return> str> (elements)>

Oto pełna implementacja klasy „HashTable”:

Python3




class> Node:> > def> __init__(> self> , key, value):> > self> .key> => key> > self> .value> => value> > self> .> next> => None> > > class> HashTable:> > def> __init__(> self> , capacity):> > self> .capacity> => capacity> > self> .size> => 0> > self> .table> => [> None> ]> *> capacity> > > def> _hash(> self> , key):> > return> hash> (key)> %> self> .capacity> > > def> insert(> self> , key, value):> > index> => self> ._hash(key)> > > if> self> .table[index]> is> None> :> > self> .table[index]> => Node(key, value)> > self> .size> +> => 1> > else> :> > current> => self> .table[index]> > while> current:> > if> current.key> => => key:> > current.value> => value> > return> > current> => current.> next> > new_node> => Node(key, value)> > new_node.> next> => self> .table[index]> > self> .table[index]> => new_node> > self> .size> +> => 1> > > def> search(> self> , key):> > index> => self> ._hash(key)> > > current> => self> .table[index]> > while> current:> > if> current.key> => => key:> > return> current.value> > current> => current.> next> > > raise> KeyError(key)> > > def> remove(> self> , key):> > index> => self> ._hash(key)> > > previous> => None> > current> => self> .table[index]> > > while> current:> > if> current.key> => => key:> > if> previous:> > previous.> next> => current.> next> > else> :> > self> .table[index]> => current.> next> > self> .size> -> => 1> > return> > previous> => current> > current> => current.> next> > > raise> KeyError(key)> > > def> __len__(> self> ):> > return> self> .size> > > def> __contains__(> self> , key):> > try> :> > self> .search(key)> > return> True> > except> KeyError:> > return> False> > > # Driver code> if> __name__> => => '__main__'> :> > > # Create a hash table with> > # a capacity of 5> > ht> => HashTable(> 5> )> > > # Add some key-value pairs> > # to the hash table> > ht.insert(> 'apple'> ,> 3> )> > ht.insert(> 'banana'> ,> 2> )> > ht.insert(> 'cherry'> ,> 5> )> > > # Check if the hash table> > # contains a key> > print> (> 'apple'> in> ht)> # True> > print> (> 'durian'> in> ht)> # False> > > # Get the value for a key> > print> (ht.search(> 'banana'> ))> # 2> > > # Update the value for a key> > ht.insert(> 'banana'> ,> 4> )> > print> (ht.search(> 'banana'> ))> # 4> > > ht.remove(> 'apple'> )> > # Check the size of the hash table> > print> (> len> (ht))> # 3>

Wyjście

True False 2 4 3 

Złożoność czasu i złożoność przestrzeni:

  • The złożoność czasu z wstawić , szukaj I usunąć Metody w tabeli mieszającej wykorzystujące oddzielne łańcuchy zależą od rozmiaru tablicy mieszającej, liczby par klucz-wartość w tabeli mieszającej oraz długości połączonej listy w każdym indeksie.
  • Zakładając dobrą funkcję skrótu i ​​równomierny rozkład kluczy, oczekiwana złożoność czasowa tych metod wynosi O(1) dla każdej operacji. Jednak w najgorszym przypadku może wystąpić złożoność czasowa NA) , gdzie n to liczba par klucz-wartość w tabeli skrótów.
  • Jednakże ważne jest, aby wybrać dobrą funkcję skrótu i ​​odpowiedni rozmiar tablicy mieszającej, aby zminimalizować prawdopodobieństwo kolizji i zapewnić dobrą wydajność.
  • The złożoność przestrzeni tabeli mieszającej przy użyciu oddzielnego łączenia łańcuchowego zależy od rozmiaru tablicy mieszającej i liczby par klucz-wartość przechowywanych w tabeli mieszającej.
  • Sama tabela mieszająca zajmuje O(m) spacja, gdzie m jest pojemnością tablicy mieszającej. Każdy połączony węzeł listy pobiera O(1) spacja, a na połączonych listach może znajdować się co najwyżej n węzłów, gdzie n to liczba par klucz-wartość przechowywanych w tabeli mieszającej.
  • Zatem całkowita złożoność przestrzeni wynosi O(m + n) .

Wniosek:

W praktyce ważne jest, aby wybrać odpowiednią pojemność tablicy mieszającej, aby zrównoważyć wykorzystanie przestrzeni i prawdopodobieństwo kolizji. Jeśli pojemność jest zbyt mała, wzrasta prawdopodobieństwo kolizji, co może spowodować pogorszenie wydajności. Z drugiej strony, jeśli pojemność jest zbyt duża, tabela mieszająca może zużywać więcej pamięci niż to konieczne.