Lassen Sie uns zunächst über den Top-K-Algorithmus sprechen. Die Details sind wie folgt
Anwendungsszenarien:
Die Suchmaschine zeichnet alle vom Benutzer für jede Suche verwendeten Suchzeichenfolgen in Protokolldateien auf. Die Länge jeder Abfragezeichenfolge beträgt 1-255 Bytes.
Angenommen, es gibt derzeit 10 Millionen Datensätze (der Wiederholungsgrad dieser Abfragezeichenfolgen ist relativ hoch, obwohl die Gesamtzahl 10 Millionen beträgt, aber wenn die Wiederholungen entfernt werden, überschreitet sie 3 Millionen nicht. Je höher der Wiederholungsgrad einer Abfrage ist Zeichenfolge bedeutet, dass die Abfragezeichenfolge umso beliebter ist, je mehr Benutzer vorhanden sind. Bitte zählen Sie die 10 beliebtesten Abfragezeichenfolgen und der erforderliche Speicher darf 1 GB nicht überschreiten.
Grundlegende Kenntnisse:
Was ist eine Hash-Tabelle?
Hash-Tabelle (Hash-Tabelle, auch Hash-Tabelle genannt) ist eine Datenstruktur, auf die basierend auf dem Schlüsselwert direkt zugegriffen wird.
Das heißt, es greift auf den Datensatz zu, indem der Schlüsselwert einer Position in der Tabelle zugeordnet wird, um die Suche zu beschleunigen. Diese Zuordnungsfunktion wird als Hash-Funktion bezeichnet, und das Array, in dem die Datensätze gespeichert sind, wird als Hash-Tabelle bezeichnet.
Die Methode der Hash-Tabelle ist eigentlich sehr einfach. Sie besteht darin, den Schlüssel über eine feste Algorithmusfunktion, die sogenannte Hash-Funktion, in eine Ganzzahl umzuwandeln und die Zahl dann auf die Länge des Arrays zu modulieren Ergebnis ist Als Index des Arrays wird der Wert im Array-Bereich mit der Zahl als Index gespeichert.
Wenn eine Hash-Tabelle für die Abfrage verwendet wird, wird die Hash-Funktion erneut verwendet, um den Schlüssel in den entsprechenden Array-Index umzuwandeln und den Platz zu lokalisieren, um den Wert zu erhalten. Auf diese Weise kann die Positionierungsleistung des Arrays vollständig für die Datenverarbeitung genutzt werden .Position.
Problemanalyse:
Um die beliebtesten Abfragen zu zählen, zählen Sie zunächst die Häufigkeit jeder Abfrage und ermitteln Sie dann die Top 10 anhand der statistischen Ergebnisse. Basierend auf dieser Idee können wir den Algorithmus also in zwei Schritten entwerfen.
Das heißt, die Lösung dieses Problems gliedert sich in die folgenden zwei Schritte:
Schritt eins: Abfragestatistiken (zählen Sie die Anzahl der Vorkommen jeder Abfrage)
Für die Abfragestatistik stehen die folgenden zwei Methoden zur Auswahl:
1), direkte Sortiermethode (häufig Statistiken in der Protokolldatei, verwenden Sie Cat File | Format Key | Sort | Uniq -C | Sort -NR | Head -N 10, das ist die Methode)
Der Algorithmus, an den wir zuerst denken, ist das Sortieren. Zuerst sortieren wir alle Abfragen im Protokoll, durchlaufen dann die sortierten Abfragen und zählen, wie oft jede Abfrage erscheint.
Aber es gibt klare Anforderungen, das heißt, der Speicher darf 1 GB nicht überschreiten, und jeder Datensatz ist 2,375 GB groß. Diese Bedingung erfüllt die Anforderungen nicht .
Erinnern wir uns an den Inhalt des Datenstrukturkurses. Wenn die Datenmenge relativ groß ist und nicht im Speicher untergebracht werden kann, können wir die Zusammenführungssortierung verwenden Bessere Zeitkomplexität O(NlgN).
Nach dem Sortieren durchlaufen wir die geordnete Abfragedatei, zählen die Anzahl der Vorkommen jeder Abfrage und schreiben sie erneut in die Datei.
Eine umfassende Analyse zeigt, dass die zeitliche Komplexität der Sortierung O(NlgN) und die zeitliche Komplexität der Durchquerung O(N) beträgt, sodass die Gesamtzeitkomplexität des Algorithmus O(N NlgN)=O(NlgN) beträgt. .
2), Hash-Tabellenmethode (diese Methode eignet sich sehr gut zum Zählen der Anzahl des Vorkommens einer Zeichenfolge)
Bei der ersten Methode verwenden wir eine Sortiermethode, um die Anzahl der Vorkommen jeder Abfrage zu zählen. Die Zeitkomplexität beträgt NlgN. Gibt es also eine bessere Möglichkeit, sie mit geringerer Zeitkomplexität zu speichern?
Im Titel wird erklärt, dass es aufgrund des relativ hohen Wiederholungsgrads tatsächlich nur 3 Millionen Abfragen gibt, wobei jede Abfrage 255 Byte groß ist, sodass wir darüber nachdenken können, sie alle in den Speicher aufzunehmen Wir benötigen lediglich eine geeignete Datenstruktur. Hier ist Hash Table definitiv unsere vorrangige Wahl, da die Abfragegeschwindigkeit von Hash Table sehr hoch ist und die Zeitkomplexität fast O(1) beträgt.
So, wir haben unseren Algorithmus:
Pflegen Sie eine HashTable, deren Schlüssel die Abfragezeichenfolge ist und deren Wert die Anzahl der Vorkommen der Abfrage ist. Wenn die Zeichenfolge nicht in der Tabelle enthalten ist, fügen Sie die Zeichenfolge hinzu und setzen Sie den Wert auf 1 Die Zeichenfolge befindet sich in der Tabelle. Fügen Sie dann eins zur Anzahl der Zeichenfolge hinzu. Schließlich haben wir die Verarbeitung dieser riesigen Daten innerhalb einer Zeitkomplexität von O(N) abgeschlossen.
Berbanding dengan Algoritma 1, kaedah ini meningkatkan kerumitan masa mengikut susunan magnitud, iaitu O(N), tetapi ia bukan sahaja pengoptimuman dalam kerumitan masa, kaedah ini hanya perlu IO fail data sekali, manakala Algoritma 1 Bilangan IO adalah tinggi, jadi Algoritma 2 mempunyai kebolehkendalian yang lebih baik dalam kejuruteraan daripada Algoritma 1.
Langkah 2: Cari 10 Teratas (cari 10 yang paling kerap muncul)
Algoritma 1: Isih biasa (kita hanya perlu mencari 10 teratas, jadi semua pengisihan adalah berlebihan)
Saya rasa semua orang sudah biasa dengan algoritma pengisihan, jadi saya tidak akan membincangkan butiran di sini. Apa yang perlu kita ambil perhatian ialah kerumitan masa algoritma pengisihan ialah NlgN. Dalam soalan ini, tiga juta rekod boleh disimpan dengan 1G ingatan.
Algoritma 2: Isih Separa
Keperluan soalan adalah untuk mencari 10 Teratas, jadi kami tidak perlu mengisih semua Pertanyaan Kami hanya perlu mengekalkan tatasusunan 10 saiz, memulakan 10 Pertanyaan dan mengisih setiap Pertanyaan daripada besar kepada kecil mengikut bilangan statistik. . , dan kemudian merentasi 3 juta rekod Setiap kali rekod dibaca, ia dibandingkan dengan Pertanyaan terakhir dalam tatasusunan Jika ia lebih kecil daripada Pertanyaan ini, maka teruskan merentasi dihapuskan (ia masih perlu diletakkan pada kedudukan yang sesuai untuk menyimpannya di sana. urutan), tambah Pertanyaan semasa. Akhirnya, apabila semua data telah dilalui, 10 Pertanyaan dalam tatasusunan ini akan menjadi Top10 yang kami cari.
Tidak sukar untuk menganalisis bahawa dengan cara ini, kerumitan masa yang paling teruk bagi algoritma ialah N*K, di mana K merujuk kepada nombor teratas.
Algoritma 3: Timbunan
Dalam Algoritma 2, kami telah mengoptimumkan kerumitan masa dari NlogN ke N*K Saya harus mengatakan bahawa ini adalah peningkatan yang agak besar, tetapi adakah cara yang lebih baik?
Analisisnya, dalam Algoritma 2, selepas setiap perbandingan selesai, kerumitan operasi yang diperlukan ialah K, kerana elemen perlu dimasukkan ke dalam jadual linear, dan perbandingan berjujukan digunakan. Mari kita ambil perhatian di sini bahawa tatasusunan dipesan Setiap kali kita mencari, kita boleh menggunakan kaedah binari untuk mencari Dengan cara ini, kerumitan operasi dikurangkan kepada logK Walau bagaimanapun, masalah seterusnya adalah pergerakan data bilangan data telah meningkat. Walau bagaimanapun, algoritma ini masih ditambah baik berbanding Algoritma 2.
Berdasarkan analisis di atas, mari kita fikirkan, adakah terdapat struktur data yang boleh mencari dan memindahkan elemen dengan cepat?
Jawapannya adalah ya, itu adalah timbunan.
Dengan bantuan struktur timbunan, kita boleh mencari dan menyesuaikan/bergerak dalam masa log. Jadi pada ketika ini, algoritma kami boleh diperbaiki kepada ini, mengekalkan timbunan akar kecil bersaiz K (10 dalam soalan ini), kemudian merentasi 3 juta Pertanyaan, dan membandingkannya dengan elemen akar masing-masing.
Idea ini konsisten dengan Algoritma 2 yang disebutkan di atas, kecuali dalam Algoritma 3, kami menggunakan struktur data seperti timbunan minimum dan bukannya tatasusunan, yang mengurangkan kerumitan masa mencari elemen sasaran daripada O(K ) kepada O(logK).
Jadi, menggunakan struktur data timbunan dan Algoritma 3, kerumitan masa akhir dikurangkan kepada N*logK Berbanding dengan Algoritma 2, terdapat peningkatan yang agak besar.
Pada ketika ini, algoritma telah tamat sepenuhnya Selepas langkah pertama di atas, mula-mula gunakan jadual Hash untuk mengira bilangan kejadian setiap Pertanyaan, O(N); kemudian dalam langkah kedua, gunakan struktur data timbunan untuk mencari Top 10, N* O(logK). Jadi, kerumitan masa terakhir kami ialah: O(N) N'*O(logK). (N ialah 10 juta, N' ialah 3 juta).
Bagaimanakah js menggunakan heap untuk melaksanakan algoritma Top K?
1. Gunakan algoritma timbunan untuk melaksanakan Atas, kerumitan masa ialah O(LogN)
function top(arr,comp){ if(arr.length == 0){return ;} var i = arr.length / 2 | 0 ; for(;i >= 0; i--){ if(comp(arr[i], arr[i * 2])){exch(arr, i, i*2);} if(comp(arr[i], arr[i * 2 + 1])) {exch(arr, i, i*2 + 1);} } return arr[0]; } function exch(arr,i,j){ var t = arr[i]; arr[i] = arr[j]; arr[j] = t; }
2 Panggil pelaksanaan timbunan K kali, kerumitan masa ialah O(K * LogN)
function topK(arr,n,comp){ if(!arr || arr.length == 0 || n <=0 || n > arr.length){ return -1; } var ret = new Array(); for(var i = 0;i < n; i++){ var max = top(arr,comp); ret.push(max); arr.splice(0,1); } return ret; }
3. Ujian
var ret = topK(new Array(16,22,91,0,51,44,23),3,function (a,b){return a < b;}); console.log(ret);
Di atas ialah pelaksanaan algoritma Top K menggunakan heap Apakah algoritma Top K? Saya harap ia akan membantu pembelajaran semua orang.