Angenommen, Sie sind Eigentümer eines großen Provisionsgeschäfts in dem Landkreis, in dem Sie leben. Angenommen, Sie leben in einem großen Gebiet, das kein Gewerbegebiet ist. Sie sind nicht der einzige mit einem Provisionsgeschäft in der Gegend. Sie haben ein paar Konkurrenten. Und dann fällt Ihnen ein, dass Sie die Telefonnummern Ihrer Kunden in einem Übungsbuch aufzeichnen sollten. Natürlich ist das Trainingsbuch klein und Sie können nicht alle Telefonnummern für alle Ihre Kunden aufzeichnen.
Sie beschließen also, nur die Telefonnummern Ihrer Stammkunden aufzuzeichnen. Und so haben Sie eine Tabelle mit zwei Spalten. Die Spalte links enthält die Namen der Kunden, und die Spalte rechts hat die entsprechenden Telefonnummern. Auf diese Weise gibt es eine Zuordnung zwischen Kundennamen und Telefonnummern. Die rechte Spalte der Tabelle kann als Kernhash -Tabelle betrachtet werden. Kundennamen werden jetzt als Schlüssel bezeichnet, und die Telefonnummern werden als Werte bezeichnet. Beachte. Beachten Sie auch, dass die Anzahl der Stammkunden mit der Zeit erhöht oder verringert wird, und so kann die Tabelle wachsen oder schrumpfen.
Angenommen, es gibt als ein weiteres Beispiel für die Kartierung, dass es einen Bauernclub in einer Grafschaft gibt. Natürlich werden nicht alle Bauern Mitglieder des Clubs sein. Einige Mitglieder des Clubs werden keine regulären Mitglieder sein (anwesend und Beitrag). Der Bar-Mann kann sich entscheiden, die Namen der Mitglieder und ihre Auswahl des Getränks aufzuzeichnen. Er entwickelt eine Tabelle von zwei Spalten. In der linken Spalte schreibt er die Namen der Clubmitglieder. In der rechten Spalte schreibt er die entsprechende Wahl des Getränks.
Hier gibt es ein Problem: In der rechten Spalte befinden sich Duplikate. Das heißt, der gleiche Name eines Getränks wird mehr als einmal gefunden. Mit anderen Worten, verschiedene Mitglieder trinken das gleiche süße Getränk oder das gleiche alkoholische Getränk, während andere Mitglieder ein anderes süßes oder alkoholisches Getränk trinken. Der Bar-Mann beschließt, dieses Problem zu lösen, indem er eine schmale Säule zwischen den beiden Säulen einfügt. In dieser mittleren Spalte, beginnend von oben, zählt er die Zeilen von Null (ich.e. 0, 1, 2, 3, 4 usw.), untergehen, einen Index pro Zeile. Damit wird sein Problem gelöst, da ein Mitgliedsname jetzt einen Index kartiert und nicht zum Namen eines Getränks. Wenn ein Getränk durch einen Index identifiziert wird, wird ein Kundenname dem entsprechenden Index zugeordnet.
Die Wertsäule (Getränke) allein bildet die grundlegende Hash -Tabelle. In der modifizierten Tabelle bilden die Indizesäule und deren zugehörigen Werten (mit oder ohne Duplikate) eine normale Hash -Tabelle - die vollständige Definition einer Hash -Tabelle ist unten angegeben. Die Schlüssel (erste Spalte) sind nicht unbedingt einen Teil der Hash -Tabelle.
Betrachten Sie als ein weiteres Beispiel erneut einen Netzwerkserver, auf dem ein Benutzer von seinem Client -Computer einige Informationen hinzufügen, einige Informationen löschen oder einige Informationen ändern kann. Es gibt viele Benutzer für den Server. Jeder Benutzername entspricht einem Passwort, das auf dem Server gespeichert ist. Diejenigen, die den Server pflegen, können die Benutzernamen und das entsprechende Passwort sehen und können die Arbeit der Benutzer beschädigen.
Daher beschließt der Eigentümer des Servers, eine Funktion zu erstellen, die ein Passwort verschlüsselt, bevor er gespeichert wird. Ein Benutzer meldet sich mit seinem normal verstandenen Passwort auf dem Server an. Jetzt wird jedes Passwort jedoch in verschlüsselter Form gespeichert. Wenn jemand ein verschlüsseltes Passwort sieht und versucht, sich mit es anzumelden.
In diesem Fall ist das verstandenes Passwort der Schlüssel, und das verschlüsselte Passwort ist der Wert. Wenn sich das verschlüsselte Passwort in einer Spalte mit verschlüsselten Passwörtern befindet, ist diese Spalte eine grundlegende Hash -Tabelle. Wenn dieser Spalte eine andere Spalte mit Indizes vorausgeht, die von Null beginnen, so dass jedes verschlüsselte Passwort zugeordnet ist, bilden Sie sowohl die Indizesspalte als auch die Spalte für verschlüsselte Kennwort eine normale Hash -Tabelle. Die Schlüssel sind nicht unbedingt Teil der Hash -Tabelle.
Beachten Sie in diesem Fall, dass jeder Schlüssel, der ein verstandenes Passwort ist, einem Benutzernamen entspricht. Es gibt also einen Benutzernamen, der einem Schlüssel entspricht, der einem Index zugeordnet ist, der einem Wert zugeordnet ist, der ein verschlüsselter Schlüssel ist.
Die Definition einer Hash -Funktion, die vollständige Definition einer Hash -Tabelle, die Bedeutung eines Arrays und andere Details finden Sie unten. Sie müssen Kenntnisse in Zeigern (Referenzen) und verknüpften Listen haben, um den Rest dieses Tutorials zu schätzen.
Bedeutung der Hash -Funktion und Hash -Tabelle
Array
Ein Array ist eine Reihe aufeinanderfolgender Speicherorte. Alle Standorte haben die gleiche Größe. Der Wert am ersten Ort wird mit dem Index 0 zugegriffen. Der Wert im zweiten Standort wird mit dem Index 1 zugegriffen; Der dritte Wert wird mit dem Index 2 zugegriffen; Viertens mit Index, 3; usw. Ein Array kann normalerweise nicht erhöhen oder schrumpfen. Um die Größe (Länge) eines Arrays zu ändern, muss ein neues Array erstellt werden, und entsprechende Werte,. Die Werte eines Arrays sind immer vom gleichen Typ.
Hash-Funktion
In der Software ist eine Hash -Funktion eine Funktion, die einen Schlüssel übernimmt und einen entsprechenden Index für eine Array -Zelle erzeugt. Das Array hat eine feste Größe (feste Länge). Die Anzahl der Schlüssel ist von willkürlicher Größe, normalerweise größer als die Größe des Arrays. Der aus der Hash -Funktion resultierende Index wird als Hash -Wert oder Digest oder Hash -Code oder einfach als Hash bezeichnet.
Hash-tabelle
Eine Hash -Tabelle ist ein Array mit Werten, zu deren Indizes die Schlüssel zugeordnet sind. Die Schlüssel werden indirekt den Werten zugeordnet. Tatsächlich sollen die Schlüssel den Werten zugeordnet werden, da jeder Index mit einem Wert verbunden ist (mit oder ohne Duplikate). Die Funktion, die Mapping (i "macht.e. Hashing) bezieht Tasten mit den Array -Indizes und nicht wirklich auf die Werte, da es möglicherweise in den Werten Duplikate gibt. Das folgende Diagramm zeigt eine Hash -Tabelle für die Namen von Personen und ihre Telefonnummern. Die Array -Zellen (Slots) werden als Eimer bezeichnet.
Beachten Sie, dass einige Eimer leer sind. Eine Hash -Tabelle darf nicht unbedingt Werte in allen Eimern haben. Die Werte in den Eimern dürfen nicht unbedingt in aufsteigender Reihenfolge sein. Die Indizes, mit denen sie verbunden sind, sind jedoch in aufsteigender Reihenfolge. Die Pfeile geben die Zuordnung an. Beachten Sie, dass sich die Schlüssel nicht in einem Array befinden. Sie müssen in keiner Struktur sein. Eine Hash -Funktion nimmt jeden Schlüssel und Hashes einen Index für ein Array aus. Wenn der mit dem Indexhashed zugeordnete Wert keinen Wert gibt, kann ein neuer Wert in diesen Eimer eingebracht werden. Die logische Beziehung liegt zwischen dem Schlüssel und dem Index und nicht zwischen dem Schlüssel und dem mit dem Index zugeordneten Wert.
Die Werte eines Arrays, wie die dieser Hash -Tabelle, sind immer vom gleichen Datentyp. Eine Hash -Tabelle (Eimer) kann Schlüssel mit den Werten verschiedener Datentypen verbinden. In diesem Fall sind die Werte des Arrays alle Zeiger, die auf verschiedene Werttypen hinweisen.
Eine Hash -Tabelle ist ein Array mit einer Hash -Funktion. Die Funktion nimmt einen Schlüssel und hasht einen entsprechenden Index und verbindet die Schlüssel mit Werten im Array. Die Schlüssel müssen nicht Teil der Hash -Tabelle sein.
Warum Array und keine verknüpfte Liste für die Hash -Tabelle
Das Array für eine Hash -Tabelle kann durch eine verknüpfte Listendatenstruktur ersetzt werden, aber es würde ein Problem geben. Das erste Element einer verknüpften Liste ist natürlich bei Index, 0; Das zweite Element ist natürlich im Index 1; Der dritte ist natürlich bei Index 2; usw. Das Problem mit der verlinkten Liste ist, dass die Liste zum Abrufen eines Wertes durchgesetzt werden muss, und dies braucht Zeit. Der Zugriff auf einen Wert in einem Array erfolgt nach Zufallszugriff. Sobald der Index bekannt ist, wird der Wert ohne Iteration erhalten; Dieser Zugang ist schneller.
Kollision
Die Hash -Funktion hat einen Schlüssel und Hashes den entsprechenden Index, um den zugehörigen Wert zu lesen oder einen neuen Wert einzufügen. Wenn der Zweck darin besteht, einen Wert zu lesen, gibt es bisher kein Problem (kein Problem). Wenn der Zweck jedoch darin besteht, einen Wert einzufügen, hat der Hashed -Index möglicherweise bereits einen zugeordneten Wert, und das ist eine Kollision. Der neue Wert kann nicht dort festgelegt werden, wo es bereits einen Wert gibt. Es gibt Möglichkeiten zur Lösung von Kollision - siehe unten.
Warum Kollision auftritt
Im obigen Beispiel für Provision -Shops sind die Kundennamen die Schlüssel, und die Namen der Getränke sind die Werte. Beachten Sie, dass die Kunden zu viele sind, während das Array eine begrenzte Größe hat und nicht alle Kunden mitnehmen kann. Also werden nur die Getränke von Stammkunden in der Array gespeichert. Die Kollision würde auftreten, wenn ein nicht regulärer Kunde regelmäßig wird. Kunden für das Laden bilden ein großes Set, während die Anzahl der Eimer für Kunden im Array begrenzt ist.
Bei Hash -Tabellen sind es die Werte für die Schlüssel, die sehr wahrscheinlich sind, die aufgezeichnet werden. Wenn ein Schlüssel, der wahrscheinlich nicht war, wahrscheinlich, würde es wahrscheinlich eine Kollision geben. In der Tat tritt Kollision immer mit Hash -Tabellen auf.
Grundlagen der Kollisionsauflösung
Zwei Ansätze zur Kollisionsauflösung werden als separate Verkettung und offene Adressierung bezeichnet. Theoretisch sollten die Schlüssel nicht in der Datenstruktur liegen oder nicht Teil der Hash -Tabelle sein. Beide Ansätze erfordern jedoch, dass die Schlüsselsäule der Hash -Tabelle vorausgeht und Teil der Gesamtstruktur wird. Anstelle von Schlüssel in der Spalte der Schlüsseln sind Hinweise auf die Tasten in der Spalte der Schlüsseln.
Eine praktische Hash -Tabelle enthält eine Schlüsselspalte, aber diese Schlüsselspalte ist nicht offiziell Teil der Hash -Tabelle.
Beide Ansätze für die Auflösung können leere Eimer haben, nicht unbedingt am Ende des Arrays.
Getrennte Verkettung
Bei der separaten Verkettung wird bei einer Kollision der neue Wert nach rechts (nicht über oder unter) des Collided -Werts hinzugefügt. Zwei oder drei Werte haben also denselben Index. Selten sollten mehr als drei den gleichen Index haben.
Kann mehr als ein Wert in einem Array wirklich den gleichen Index haben? - NEIN. In vielen Fällen ist der erste Wert für den Index ein Zeiger auf eine verknüpfte Listendatenstruktur, die die einen, zwei oder drei Collided -Werte enthält. Das folgende Diagramm ist ein Beispiel für eine Hash -Tabelle für die separate Verkettung von Kunden und deren Telefonnummern:
Die leeren Eimer sind mit dem Buchstaben x markiert. Der Rest der Slots hat Hinweise auf verknüpfte Listen. Jedes Element der verknüpften Liste enthält zwei Datenfelder: eines für den Kundennamen und das andere für die Telefonnummer. Konflikt tritt für die Schlüssel auf: Peter Jones und Suzan Lee. Die entsprechenden Werte bestehen aus zwei Elementen einer verknüpften Liste.
Für widersprüchliche Schlüsseln ist das Kriterium zum Einfügen von Wert das gleiche Kriterium, das zum Auffinden (und Lesen) des Wertes verwendet wird.
Offene Adressierung
Bei offener Adressierung werden alle Werte im Eimer -Array gespeichert. Wenn Konflikte auftreten, wird der neue Wert in einen leeren Eimer neu eingefügt Der entsprechende Wert für den Konflikt nach einigen Kriterien. Das Kriterium zum Einfügen eines Wertes bei Konflikt.
Das folgende Diagramm zeigt die Konfliktlösung mit offener Adressierung:
Die Hash -Funktion übernimmt den Schlüssel, Peter Jones und Hashes den Index 152, und speichert seine Telefonnummer im zugehörigen Bucket. Nach einiger Zeit hashes der Hash -Funktion der gleiche Index, 152 aus dem Schlüssel, Suzan Lee, kollidiert mit dem Index für Peter Jones. Um dies zu beheben, wird der Wert für Suzan Lee im Eimer des nächsten Index, 153, gespeichert, der leer war. Die Hash -Funktion Hashes Der Index 153 für den Schlüssel Robin Hood, dieser Index wurde jedoch bereits verwendet, um den Konflikt für einen früheren Schlüssel zu beheben. Der Wert für Robin Hood wird also in den nächsten leeren Eimer platziert, der von Index 154 ist.
Methoden zur Lösung von Konflikten für separate Verkettung und offene Adressierung
Separate Verkettung hat seine Methoden zur Lösung von Konflikten und die offene Adressierung hat auch seine eigenen Methoden zur Lösung von Konflikten.
Methoden zur Lösung separater Verkettungskonflikte
Die Methoden für separate Hash -Tabellen werden jetzt kurz erklärt:
Separate Verkettung mit verknüpften Listen
Diese Methode ist wie oben erläutert. Jedes Element der verlinkten Liste darf jedoch nicht unbedingt das Schlüsselfeld haben (e.G. Kundenname Feld oben).
Separate Verkettung mit Listenkopfzellen
In dieser Methode wird das erste Element der verknüpften Liste in einem Eimer des Arrays gespeichert. Dies ist möglich, wenn der Datentyp für das Array das Element der verknüpften Liste ist.
Separate Verkettung mit anderen Strukturen
Jede andere Datenstruktur wie der selbstausgleichende binäre Suchbaum, der die erforderlichen Operationen unterstützt, kann anstelle der verknüpften Liste verwendet werden - siehe später.
Methoden zur Lösung offener Adressierungskonflikte
Eine Methode zur Lösung von Konflikten in der offenen Adressierung wird als Sondensequenz bezeichnet. Drei bekannte Sondensequenzen werden jetzt kurz erklärt:
Lineare Untersuchung
Bei einer linearen Untersuchung wird bei einem Konflikt der nächste leere Eimer unter dem Eimer im Konflikt gesucht. Auch bei linearer Prüfung werden sowohl der Schlüssel als auch sein Wert im selben Eimer gespeichert.
Quadratische Prüfung
Angenommen, dieser Konflikt erfolgt im Index H. Der nächste leere Steckplatz (Eimer) am Index H + 12 wird eingesetzt; Wenn das bereits besetzt ist, dann leer die nächste leer bei H + 22 wird verwendet, wenn das bereits besetzt ist, dann der nächste leer bei H + 32 wird verwendet und so weiter. Dies gibt Varianten.
Double Hashing
Mit Double Hashing gibt es zwei Hash -Funktionen. Der erste berechnet (Hashes) den Index. Wenn ein Konflikt auftritt, verwendet der zweite den gleichen Schlüssel, um festzustellen, wie weit der Wert unten eingefügt werden sollte. Daraufhin gibt es noch mehr - sehen Sie später.
Perfekte Hash -Funktion
Eine perfekte Hash -Funktion ist eine Hash -Funktion, die nicht zu einer Kollision führen kann. Dies kann passieren, wenn der Schlüssel der Schlüssel relativ gering ist und jede Schlüssel zu einer bestimmten Ganzzahl in der Hash -Tabelle kartiert.
Im ASCII -Zeichensatz können obere Fallzeichen ihren entsprechenden unteren Fallbuchstaben unter Verwendung einer Hash -Funktion zugeordnet werden. Buchstaben werden im Computerspeicher als Zahlen dargestellt. In ASCII -Zeichensatz ist A 65, B 66, c 67 usw. ist. und a ist 97, B ist 98, c ist 99 usw. Um von a nach a zu martieren, fügen Sie 32 zu 65 hinzu; Um von B nach B zu mart, fügen Sie 32 zu 66 hinzu; Um von C bis c zu martieren, fügen Sie 32 zu 67 hinzu; usw. Hier sind die oberen Fallbuchstaben die Schlüssel und die unteren Fallbuchstaben sind die Werte. Die Hash -Tabelle dafür kann ein Array sein, dessen Werte die zugehörigen Indizes sind. Denken Sie daran, Eimer des Arrays können leer sein. Also können Eimer im Array von 64 bis 0 leer sein. Die Hash -Funktion fügt der oberen Fallcode -Nummer einfach 32 hinzu, um den Index zu erhalten, und daher das untere Fallschreiben. Eine solche Funktion ist eine perfekte Hash -Funktion.
Hashing von Ganzzahl bis hin zu Ganzzahlindizes
Es gibt verschiedene Methoden für Hashing Ganzzahl. Eine von ihnen wird als Modulo -Divisionsmethode (Funktion) bezeichnet.
Die Hashing -Funktion der Modulo Division
Eine Funktion in Computersoftware ist keine mathematische Funktion. In Computing (Software) besteht eine Funktion aus einer Reihe von Aussagen, denen Argumente vorangegangen sind. Für die Modulo -Divisionsfunktion sind die Tasten Ganzzahlen und werden auf Indizes des Schallscheibens zugeordnet. Der Schlüssel der Schlüssel ist groß, daher würden nur Schlüssel, die in der Aktivität auftreten, nur zugeordnet. Kollisionen treten also auf, wenn unwahrscheinliche Schlüssel zugeordnet werden müssen.
In der Aussage,
20/6 = 3R2
20 ist die Dividende, 6 ist der Divisor und 3 Rest 2 ist der Quotient. Der Rest 2 wird auch als Modulo bezeichnet. Hinweis: Es ist möglich, ein Modulo von 0 zu haben.
Für dieses Hashing ist die Tischgröße normalerweise eine Kraft von 2, e.G. 64 = 26 oder 256 = 28, usw. Der Divisor für diese Hashing -Funktion ist eine Primzahl in der Nähe der Arraygröße. Diese Funktion teilt den Schlüssel durch den Divisor und gibt den Modulo zurück. Das Modulo ist der Index des Schallendrafts. Der zugeordnete Wert im Eimer ist ein Wert Ihrer Wahl (Wert für den Schlüssel).
Hashing Variable Längenschlüssel
Hier sind Schlüssel des Schlüsselsatzes Texte unterschiedlicher Längen. Unter Verwendung der gleichen Anzahl von Bytes können verschiedene ganze Zahlen im Speicher gespeichert werden (die Größe eines englischen Charakters ist ein Byte). Wenn unterschiedliche Tasten unterschiedliche Bytegrößen haben, sollen sie von variabler Länge sind. Eine der Methoden zur Hashing -variablen Längen wird als Radix Conversion Hashing bezeichnet.
Radix Conversion Hashing
In einer Zeichenfolge ist jedes Zeichen im Computer eine Nummer. In dieser Methode,
Hashcode (Index) = x0AK -1+X1AK -2+… +XK -2A1+XK -1A0
Wo (x0, x1,…, xk - 1) die Zeichen der Eingangszeichenfolge und A ein Radix sind, e.G. 29 (siehe später). K ist die Anzahl der Zeichen in der Zeichenfolge. Daraufhin gibt es noch mehr - sehen Sie später.
Schlüssel und Werte
In einem Schlüssel-/Wertpaar ist ein Wert möglicherweise nicht unbedingt eine Zahl oder einen Text. Es kann auch eine Aufzeichnung sein. Ein Datensatz ist eine horizontal geschriebene Liste. In einem Schlüssel-/Wertpaar kann sich jeder Schlüssel tatsächlich auf einen anderen Text oder eine andere Nummer oder einen anderen Datensatz beziehen.
Assoziatives Array
Eine Liste handelt. Jeder Listenelement kann aus einem Paar Elemente bestehen. Die allgemeine Hash -Tabelle mit ihren Schlüssel kann als Datenstruktur betrachtet werden, ist jedoch eher ein System als eine Datenstruktur. Die Schlüssel und ihre entsprechenden Werte sind nicht sehr voneinander abhängig. Sie sind nicht sehr miteinander verwandt.
Andererseits ist ein assoziatives Array eine ähnliche Sache, aber Schlüssel und ihre Werte sind sehr voneinander abhängig. Sie sind sehr miteinander verwandt. Zum Beispiel können Sie eine assoziative Reihe von Früchten und ihre Farben haben. Jede Frucht hat natürlich ihre Farbe. Der Name der Frucht ist der Schlüssel; Die Farbe ist der Wert. Während des Einsetzens wird jeder Schlüssel mit seinem Wert eingefügt. Beim Löschen wird jeder Schlüssel mit seinem Wert gelöscht.
Ein assoziatives Array ist eine Hash -Tabellendatenstruktur, die aus Schlüssel-/Wertpaaren besteht, wobei kein Duplikat für die Schlüssel vorhanden ist. Die Werte können Duplikate haben. In dieser Situation sind die Schlüssel Teil der Struktur. Das heißt, die Schlüssel müssen gespeichert werden, während die Schlüssel mit dem allgemeinen Hast -Tisch nicht gespeichert werden müssen. Das Problem der doppelten Werte wird natürlich durch die Indizes des Schalenarrays gelöst. Verwechseln Sie nicht zwischen doppelten Werten und Kollision in einem Index.
Da ein assoziatives Array eine Datenstruktur ist, hat IS mindestens die folgenden Operationen:
Assoziative Array -Operationen
Einfügen oder hinzufügen
Dadurch wird ein neues Schlüssel-/Wertpaar in die Sammlung eingefügt, wobei der Schlüssel zu seinem Wert abgebildet ist.
neu zuweisen
Dieser Vorgang ersetzt den Wert eines bestimmten Schlüssels zu einem neuen Wert.
löschen oder entfernen
Dies beseitigt einen Schlüssel zuzüglich des entsprechenden Wertes.
Nachschlagen
Diese Operation sucht nach dem Wert eines bestimmten Schlüssels und gibt den Wert zurück (ohne ihn zu entfernen).
Abschluss
Eine Hash -Tabellendatenstruktur besteht aus einem Array und einer Funktion. Die Funktion wird als Hash -Funktion bezeichnet. Die Funktion bildet Schlüssel zu Werten im Array durch die Indizes des Arrays. Die Schlüssel dürfen nicht unbedingt Teil der Datenstruktur sein. Der Schlüsselsatz ist normalerweise größer als die gespeicherten Werte. Wenn eine Kollision auftritt. Ein assoziatives Array ist ein Sonderfall der Hash -Tabellendatenstruktur.