Ich habe dieses Problem durch ein Interview mit Microsoft erhalten.
Schreiben Sie bei einem Array zufälliger Ganzzahlen einen Algorithmus in C, der doppelte Zahlen entfernt und die eindeutigen Zahlen im ursprünglichen Array zurückgibt.
ZB Eingabe: {4, 8, 4, 1, 1, 2, 9}
Ausgabe:{4, 8, 1, 2, 9, ?, ?}
Eine Einschränkung ist, dass der erwartete Algorithmus nicht erfordern sollte, dass das Array zuerst sortiert wird. Und wenn ein Element entfernt wurde, müssen auch die folgenden Elemente nach vorne verschoben werden. Auf jeden Fall ist der Wert der Elemente am Ende des Arrays, bei dem die Elemente nach vorne verschoben wurden, vernachlässigbar.
Update: Das Ergebnis muss im ursprünglichen Array zurückgegeben werden und die Hilfsdatenstruktur (z. B. Hashtabelle) sollte nicht verwendet werden. Ich denke jedoch, dass eine Auftragserhaltung nicht erforderlich ist.
Update2: Für diejenigen, die sich fragen, warum diese unpraktischen Einschränkungen bestehen, war dies eine Interviewfrage, und all diese Einschränkungen werden während des Denkprozesses diskutiert, um zu sehen, wie ich auf verschiedene Ideen kommen kann.
quelle
Antworten:
Wie wäre es mit:
Sollte O (n ^ 2) oder weniger sein.
quelle
Eine von meiner Freundin vorgeschlagene Lösung ist eine Variation der Zusammenführungssorte. Die einzige Änderung besteht darin, dass während des Zusammenführungsschritts doppelte Werte einfach ignoriert werden. Diese Lösung wäre auch O (n log n). Bei diesem Ansatz werden das Sortieren / Entfernen von Duplikaten miteinander kombiniert. Ich bin mir jedoch nicht sicher, ob das einen Unterschied macht.
quelle
Ich habe das schon einmal auf SO gepostet, aber ich werde es hier reproduzieren, weil es ziemlich cool ist. Es verwendet Hashing und erstellt so etwas wie einen Hash-Set. Es ist garantiert O (1) im Achselraum (die Rekursion ist ein Tail Call) und hat typischerweise eine O (N) -Zeitkomplexität. Der Algorithmus ist wie folgt:
Dies kann als O (N) gezeigt werden, sofern kein pathologisches Szenario im Hashing vorliegt: Selbst wenn keine Duplikate vorhanden sind, werden bei jeder Rekursion ungefähr 2/3 der Elemente eliminiert. Jede Rekursionsebene ist O (n), wobei klein n die Anzahl der verbleibenden Elemente ist. Das einzige Problem ist, dass es in der Praxis langsamer ist als eine schnelle Sortierung, wenn nur wenige Duplikate vorhanden sind, dh viele Kollisionen. Wenn es jedoch große Mengen an Duplikaten gibt, ist dies erstaunlich schnell.
Bearbeiten: In aktuellen Implementierungen von D beträgt hash_t 32 Bit. Alles an diesem Algorithmus geht davon aus, dass es im gesamten 32-Bit-Raum nur sehr wenige, wenn überhaupt, Hash-Kollisionen geben wird. Kollisionen können jedoch häufig im Modulraum auftreten. Diese Annahme gilt jedoch aller Wahrscheinlichkeit nach für jeden Datensatz mit angemessener Größe. Wenn der Schlüssel kleiner oder gleich 32 Bit ist, kann es sich um einen eigenen Hash handeln, was bedeutet, dass eine Kollision im gesamten 32-Bit-Raum unmöglich ist. Wenn es größer ist, können Sie einfach nicht genug davon in den 32-Bit-Speicheradressraum einpassen, damit es ein Problem darstellt. Ich gehe davon aus, dass hash_t in 64-Bit-Implementierungen von D, in denen Datensätze größer sein können, auf 64 Bit erhöht wird. Sollte sich dies jemals als Problem herausstellen, könnte man die Hash-Funktion auf jeder Rekursionsstufe ändern.
Hier ist eine Implementierung in der Programmiersprache D:
quelle
Eine effizientere Implementierung
In dieser Implementierung muss das Array nicht sortiert werden. Auch wenn ein doppeltes Element gefunden wird, müssen nicht alle Elemente danach um eine Position verschoben werden.
Die Ausgabe dieses Codes ist Array [] mit der Größe NewLength
Hier beginnen wir mit dem 2. Element im Array und vergleichen es mit allen Elementen im Array bis zu diesem Array. Wir halten eine zusätzliche Indexvariable 'NewLength' zum Ändern des Eingabearrays bereit. Die Variable NewLength wird auf 0 initialisiert.
Element in Array [1] wird mit Array [0] verglichen. Wenn sie unterschiedlich sind, wird der Wert in Array [NewLength] mit Array [1] geändert und NewLength erhöht. Wenn sie gleich sind, wird NewLength nicht geändert.
Wenn wir also ein Array [1 2 1 3 1] haben, dann
Im ersten Durchgang der 'j'-Schleife wird Array [1] (2) mit Array0 verglichen, dann wird 2 in Array [NewLength] = Array [1] geschrieben, sodass Array [1 2] ist, da NewLength = 2
Im zweiten Durchgang der 'j'-Schleife wird Array [2] (1) mit Array0 und Array1 verglichen. Da Array [2] (1) und Array0 dieselbe Schleife sind, wird hier die Unterbrechung unterbrochen. Das Array ist also [1 2], da NewLength = 2 ist
und so weiter
quelle
Wenn Sie nach der überlegenen O-Notation suchen, ist es möglicherweise die beste Route, das Array mit einer O (n log n) -Sortierung zu sortieren und dann eine O (n) -Überquerung durchzuführen. Ohne zu sortieren sehen Sie O (n ^ 2).
Bearbeiten: Wenn Sie nur Ganzzahlen ausführen, können Sie auch eine Radix-Sortierung durchführen, um O (n) zu erhalten.
quelle
1. Verwenden von O (1) zusätzlichem Speicherplatz in O (n log n) Zeit
Dies ist zum Beispiel möglich:
Ich glaube, der Partner von ejel hat Recht, dass der beste Weg, dies zu tun, eine direkte Zusammenführungssortierung mit einem vereinfachten Zusammenführungsschritt wäre, und dass dies wahrscheinlich die Absicht der Frage ist, wenn Sie z. Schreiben einer neuen Bibliotheksfunktion, um dies so effizient wie möglich zu tun, ohne die Eingaben verbessern zu können, und es würde Fälle geben, in denen dies abhängig von der Art der Eingaben ohne Hash-Tabelle sinnvoll wäre. Aber ich habe das nicht wirklich überprüft.
2. Verwenden von O (viel) zusätzlichem Speicherplatz in O (n) Zeit
Dies funktioniert nur, wenn mehrere fragwürdige Annahmen zutreffen:
Es ist eine schlechte Antwort, aber wenn Sie viele Eingabeelemente haben, aber alle 8-Bit-Ganzzahlen (oder vielleicht sogar 16-Bit-Ganzzahlen) sind, könnte dies der beste Weg sein.
3. O (wenig) -ish zusätzlicher Raum, O (n) -ish Zeit
Wie # 2, aber verwenden Sie eine Hash-Tabelle.
4. Der klare Weg
Wenn die Anzahl der Elemente gering ist, ist das Schreiben eines geeigneten Algorithmus nicht sinnvoll, wenn anderer Code schneller zu schreiben und schneller zu lesen ist.
Z.B. Gehen Sie durch das Array für jedes eindeutige Element (dh das erste Element, das zweite Element (Duplikate des ersten wurden entfernt) usw.) und entfernen Sie alle identischen Elemente. O (1) zusätzlicher Raum, O (n ^ 2) Zeit.
Z.B. Verwenden Sie dazu Bibliotheksfunktionen. Effizienz hängt davon ab, welche Sie leicht zur Verfügung haben.
quelle
Nun, die grundlegende Implementierung ist recht einfach. Gehen Sie alle Elemente durch, prüfen Sie, ob die verbleibenden Elemente Duplikate enthalten, und verschieben Sie den Rest darüber.
Es ist schrecklich ineffizient und Sie könnten es durch ein Helfer-Array für die Ausgabe oder Sortier- / Binärbäume beschleunigen, aber dies scheint nicht erlaubt zu sein.
quelle
Wenn Sie C ++ verwenden dürfen, erhalten Sie die Antwort
std::sort
durch einen Aufruf von gefolgt von einem Aufruf anstd::unique
. Die zeitliche Komplexität beträgt O (N log N) für die Sortierung und O (N) für die eindeutige Durchquerung.Und wenn C ++ vom Tisch ist, gibt es nichts, was verhindert, dass dieselben Algorithmen in C geschrieben werden.
quelle
Sie könnten dies in einer einzigen Durchquerung tun, wenn Sie bereit sind, die Erinnerung zu opfern. Sie können einfach abrechnen, ob Sie eine Ganzzahl in einem Hash / assoziativen Array gesehen haben oder nicht. Wenn Sie bereits eine Zahl gesehen haben, entfernen Sie sie, während Sie fortfahren, oder verschieben Sie noch besser Zahlen, die Sie nicht gesehen haben, in ein neues Array, um eine Verschiebung des ursprünglichen Arrays zu vermeiden.
In Perl:
quelle
Der Rückgabewert der Funktion sollte die Anzahl der eindeutigen Elemente sein und alle werden an der Vorderseite des Arrays gespeichert. Ohne diese zusätzlichen Informationen wissen Sie nicht einmal, ob es Duplikate gab.
Jede Iteration der äußeren Schleife verarbeitet ein Element des Arrays. Wenn es eindeutig ist, bleibt es im vorderen Bereich des Arrays und wenn es ein Duplikat ist, wird es vom letzten unverarbeiteten Element im Array überschrieben. Diese Lösung läuft in O (n ^ 2) Zeit.
quelle
Hier ist eine Java-Version.
quelle
Hier ist meine Lösung.
quelle
Ein Array sollte natürlich von rechts nach links "durchlaufen" werden, um unnötiges Kopieren von Werten hin und her zu vermeiden.
Wenn Sie über unbegrenzten Speicher verfügen, können Sie
sizeof(type-of-element-in-array) / 8
Bytes ein Bitarray zuweisen , damit jedes Bit anzeigt, ob Sie bereits auf einen entsprechenden Wert gestoßen sind oder nicht.Wenn Sie dies nicht tun, kann ich mir nichts Besseres vorstellen, als ein Array zu durchlaufen und jeden Wert mit den darauf folgenden Werten zu vergleichen. Wenn dann ein Duplikat gefunden wird, entfernen Sie diese Werte vollständig. Dies ist irgendwo in der Nähe von O (n ^ 2) (oder O ((n ^ 2-n) / 2) ).
IBM hat einen Artikel zu einem ziemlich engen Thema.
quelle
Mal schauen:
quelle
Dies kann in einem Durchgang mit einem O (N log N) -Algorithmus und ohne zusätzlichen Speicher erfolgen.
Fahren Sie vom Element
a[1]
zum forta[N]
. Auf jeder Stufei
, alle Elemente auf der linken Seitea[i]
umfassen einen sortierten Haufen von Elementena[0]
durcha[j]
. Währenddessen verfolgt ein zweiter Indexj
, anfangs 0, die Größe des Heaps.Untersuchen
a[i]
und in den Haufen legen, die nun Elemente nimmta[0]
zua[j+1]
. Wenn beim Einfügen des Elements ein doppeltes Elementa[k]
mit demselben Wert gefunden wird, fügen Sie es nichta[i]
in den Heap ein (dh verwerfen Sie es). Andernfalls fügen Sie es in den Heap ein, der jetzt um ein Element wächst und jetzta[0]
toa[j+1]
und inkrementiertj
.Fahren Sie auf diese Weise zu inkrementieren ,
i
bis alle der Array - Elemente untersucht worden sind und in den Heap eingefügt, die Besatzungs endeta[0]
ana[j]
.j
ist der Index des letzten Elements des Heaps, und der Heap enthält nur eindeutige Elementwerte.Im Beispiel ist dies nicht genau das, wonach gefragt wurde, da das resultierende Array die ursprüngliche Elementreihenfolge beibehält. Wenn diese Anforderung jedoch gelockert wird, sollte der obige Algorithmus den Trick ausführen.
quelle
In Java würde ich es so lösen. Ich weiß nicht, wie ich das in C schreiben soll.
quelle
Wie wäre es mit folgendem?
Ich versuche, ein temporäres Array zu deklarieren und die Elemente darin zu platzieren, bevor ich alles zurück in das ursprüngliche Array kopiere.
quelle
Nach Überprüfung des Problems ist hier mein Delphi-Weg, der helfen kann
quelle
Das folgende Beispiel sollte Ihr Problem lösen:
quelle
quelle
Dies ist die naive (N * (N-1) / 2) Lösung. Es benötigt ständig zusätzlichen Platz und behält die ursprüngliche Reihenfolge bei. Es ähnelt der Lösung von @Byju, verwendet jedoch keine
if(){}
Blöcke. Außerdem wird vermieden, dass ein Element auf sich selbst kopiert wird.quelle
Dies kann in einem einzigen Durchgang erfolgen, in O (N) -Zeit in der Anzahl der Ganzzahlen in der Eingabeliste und O (N) -Speicher in der Anzahl der eindeutigen Ganzzahlen.
Gehen Sie die Liste von vorne nach hinten durch, wobei zwei Zeiger "dst" und "src" auf das erste Element initialisiert werden. Beginnen Sie mit einer leeren Hash-Tabelle mit "Ganzzahlen gesehen". Wenn die Ganzzahl bei src nicht im Hash vorhanden ist, schreiben Sie sie in den Slot bei dst und erhöhen Sie dst. Fügen Sie die Ganzzahl bei src zum Hash hinzu und erhöhen Sie dann src. Wiederholen, bis src das Ende der Eingabeliste passiert.
quelle
Fügen Sie alle Elemente in ein
binary tree the disregards duplicates
- einO(nlog(n))
. Extrahieren Sie dann alle wieder in das Array, indem Sie eine Durchquerung durchführen -O(n)
. Ich gehe davon aus, dass Sie keine Auftragserhaltung benötigen.quelle
Verwenden Sie zum Hashing den Bloom-Filter. Dadurch wird der Speicheraufwand erheblich reduziert.
quelle
In JAVA,
Ausgabe: {1, 2, 3, 4, 6, 7, 8, 9, 10}
hoffe das wird helfen
quelle
arrayInteger = {100,10,1};
Erstellen Sie eine,
BinarySearchTree
die O (n) Komplexität hat.quelle
Zunächst sollten Sie ein Array erstellen,
check[n]
wobei n die Anzahl der Elemente des Arrays ist, die Sie duplikationsfrei machen möchten, und den Wert jedes Elements (des Prüfarrays) auf 1 setzen. Verwenden Sie eine for-Schleife, um das Array mit dem zu durchlaufen Duplikate, sagen wir, sein Name istarr
, und schreiben Sie dies in die for-Schleife:Damit setzen Sie jedes Duplikat auf Null. Sie müssen also nur noch das
arr
Array durchlaufen und alles drucken, was nicht gleich Null ist. Die Reihenfolge bleibt bestehen und es dauert eine lineare Zeit (3 * n).quelle
Schreiben Sie bei einem Array von n Elementen einen Algorithmus, um alle Duplikate in der Zeit O (nlogn) aus dem Array zu entfernen.
In anderen Elementen wird im Ausgabearray mit dem 'Schlüssel' gepflegt. Angenommen, der Schlüssel hat die Länge O (n), die Zeit, die zum Sortieren des Schlüssels und des Werts benötigt wird, ist O (nlogn). Die zum Löschen aller Duplikate aus dem Array benötigte Zeit beträgt also O (nlogn).
quelle
helper data structure (e.g. hashtable) should not be used
?Dies ist, was ich habe, obwohl es die Reihenfolge, die wir in aufsteigender oder absteigender Reihenfolge sortieren können, um es zu reparieren, falsch platziert.
quelle
Es wäre cool, wenn Sie eine gute DataStructure hätten, die schnell erkennen könnte, ob sie eine Ganzzahl enthält. Vielleicht ein Baum.
quelle