Die Antwort unten lautet "Betrug", da die Operationen selbst zwar keinen Abstand zwischen den Operationen belegen, aber mehr als Leerzeichen verwenden können. An anderer Stelle in diesem Thread finden Sie eine Antwort, bei der dieses Problem nicht auftritt.O ( 1 )
Obwohl ich keine Antwort auf Ihre genaue Frage habe, habe ich einen Algorithmus gefunden, der in Zeit anstelle vonO(n). Ich glaube, das ist eng, obwohl ich keinen Beweis habe. Wenn überhaupt, zeigt der Algorithmus, dass der Versuch, eine Untergrenze vonO(n)zu beweisen,vergeblich ist. Daher kann er bei der Beantwortung Ihrer Frage hilfreich sein.O ( n--√)O ( n )O ( n )
Ich präsentiere zwei Algorithmen, der erste ist ein einfacher Algorithmus mit einer Laufzeit für Pop und der zweite mit einem O ( √)O ( n )Laufzeit für Pop. Ich beschreibe den ersten hauptsächlich wegen seiner Einfachheit, damit der zweite leichter zu verstehen ist.O ( n--√)
Um genauer zu sein: Der erste verwendet keinen zusätzlichen Platz, hat einen Worst-Case- (und amortisierten) Push und einen O ( n ) Worst-Case- (und amortisierten) Pop, aber das Worst-Case-Verhalten wird nicht immer ausgelöst. Da es keinen zusätzlichen Platz außerhalb der beiden Warteschlangen beansprucht, ist es etwas "besser" als die von Ross Snider angebotene Lösung.O ( 1 )O ( n )
Die zweite verwendet ein einzelnes ganzzahliges Feld (also zusätzliches Leerzeichen), hat einen O ( 1 ) Worst Case (und amortisiert) Push und einen O ( √)O ( 1 )O ( 1 )Pop abgeschrieben. Die Laufzeit ist daher erheblich besser als die des "einfachen" Ansatzes, benötigt jedoch zusätzlichen Platz.O ( n--√)
Der erste Algorithmus
Wir haben zwei Warteschlangen: Warteschlange und Warteschlangen s e c o n d . f i r s t wird unsere 'Push - Warteschlange' sein, während s e c o n d die Warteschlange bereits in 'Stapelordnung' sein wird.fi r s ts e c o n dfi r s ts e c o n d
- Das Drücken erfolgt durch einfaches Aufrufen des Parameters auf .fi r s t
- Das Poppen wird wie folgt durchgeführt. Wenn ist leer, wir einfach dequeue s e c o n d und das Ergebnis zurück. Andernfalls kehren wir f i r s t , hängen alle von s e c o n d bis f i r s t und Swap f i r s t und s e c o n d . Wir haben dann dequeue s efi r s ts e c o n dfi r s ts e c o n dfi r s tfi r s ts e c o n d und gib das Ergebnis der Entschlüsselung zurück.s e c o n d
C # -Code für den ersten Algorithmus
Dies könnte durchaus lesbar sein, auch wenn Sie noch nie C # gesehen haben. Wenn Sie nicht wissen, was Generika sind, ersetzen Sie einfach alle Instanzen von 'T' durch 'string' in Ihrem Kopf, um einen Stapel von Strings zu erhalten.
public class Stack<T> {
private Queue<T> first = new Queue<T>();
private Queue<T> second = new Queue<T>();
public void Push(T value) {
first.Enqueue(value);
}
public T Pop() {
if (first.Count == 0) {
if (second.Count > 0)
return second.Dequeue();
else
throw new InvalidOperationException("Empty stack.");
} else {
int nrOfItemsInFirst = first.Count;
T[] reverser = new T[nrOfItemsInFirst];
// Reverse first
for (int i = 0; i < nrOfItemsInFirst; i++)
reverser[i] = first.Dequeue();
for (int i = nrOfItemsInFirst - 1; i >= 0; i--)
first.Enqueue(reverser[i]);
// Append second to first
while (second.Count > 0)
first.Enqueue(second.Dequeue());
// Swap first and second
Queue<T> temp = first; first = second; second = temp;
return second.Dequeue();
}
}
}
Analyse
Offensichtlich funktioniert Push in Zeit. Pop kann alles im Innern berührt f i r s t und s e c o n d eine konstante Menge an Zeit, so haben wir O ( n ) im schlechtesten Fall. Der Algorithmus zeigt dieses Verhalten (zum Beispiel), wenn man n drücktO ( 1 )fi r s ts e c o n dO ( n )n Elemente auf den Stapel und dann wiederholt nacheinander einen einzigen Push- und einen einzelnen Pop-Vorgang durchführt.
Der zweite Algorithmus
Wir haben zwei Warteschlangen: Warteschlange und Warteschlangen s e c o n d . f i r s t wird unsere 'Push - Warteschlange' sein, während s e c o n dfi r s ts e c o n dfi r s ts e c o n d die Warteschlange bereits in 'Stapelordnung' sein wird.
Dies ist eine angepasste Version des ersten Algorithmus, bei der der Inhalt von nicht sofort in s e c o n d 'gemischt' wird . Stattdessen, wenn f i r s t eine ausreichend kleine Anzahl von Elementen enthält im Vergleich zu s e c o n d (nämlich der Quadratwurzel der Anzahl der Elemente in s e c o n d ), reorganisieren wir nur f i r s t in Stapelreihenfolge und verschmelze es nicht mitfi r s ts e c o n dfi r s ts e c o n ds e c o n dfi r s t .s e c o n d
- Das Drücken erfolgt weiterhin durch einfaches Aufrufen des Parameters auf .fi r s t
- Das Poppen wird wie folgt durchgeführt. Wenn ist leer, wir einfach dequeue s e c o n d und das Ergebnis zurück. Andernfalls reorganisieren wir den Inhalt von f i r s t , so dass sie in Stapel Ordnung sind. Wenn | f i r s t | < √fi r s ts e c o n dfi r s twir einfach dequeuefirstund das Ergebnis zurück. Andernfalls wir appendsecondauffirst, swapfirstundsecond, dequeuesecondund das Ergebnis zurück.| fi r s t | < | s e c o n d|-------√fi r s ts e c o n dfi r s tfi r s ts e c o n ds e c o n d
C # -Code für den ersten Algorithmus
Dies könnte durchaus lesbar sein, auch wenn Sie noch nie C # gesehen haben. Wenn Sie nicht wissen, was Generika sind, ersetzen Sie einfach alle Instanzen von 'T' durch 'string' in Ihrem Kopf, um einen Stapel von Strings zu erhalten.
public class Stack<T> {
private Queue<T> first = new Queue<T>();
private Queue<T> second = new Queue<T>();
int unsortedPart = 0;
public void Push(T value) {
unsortedPart++;
first.Enqueue(value);
}
public T Pop() {
if (first.Count == 0) {
if (second.Count > 0)
return second.Dequeue();
else
throw new InvalidOperationException("Empty stack.");
} else {
int nrOfItemsInFirst = first.Count;
T[] reverser = new T[nrOfItemsInFirst];
for (int i = nrOfItemsInFirst - unsortedPart - 1; i >= 0; i--)
reverser[i] = first.Dequeue();
for (int i = nrOfItemsInFirst - unsortedPart; i < nrOfItemsInFirst; i++)
reverser[i] = first.Dequeue();
for (int i = nrOfItemsInFirst - 1; i >= 0; i--)
first.Enqueue(reverser[i]);
unsortedPart = 0;
if (first.Count * first.Count < second.Count)
return first.Dequeue();
else {
while (second.Count > 0)
first.Enqueue(second.Dequeue());
Queue<T> temp = first; first = second; second = temp;
return second.Dequeue();
}
}
}
}
Analyse
Offensichtlich funktioniert Push in Zeit.O ( 1 )
Pop arbeitet in Amortisierte Zeit Es gibt zwei Fälle: if| first| < √O(n−−√)Und mische dann wirfirstin Stapelordnung inO(|first|)=O(√|first|<|second|−−−−−−−√firstzeit. Wenn| first| ≥ √O(|first|)=O(n−−√), dann müssen wir mindestens√gehabt haben|first|≥|second|−−−−−−−√ ruft nach Push. Daher können wir diesen Fall nur alle √ treffenn−−√ Anrufe zu Push and Pop. Die tatsächliche Laufzeit für diesen Fall istO(n), die amortisierte Zeit ist alsoO( n)n−−√O(n).O(nn√)=O(n−−√)
Schlussbemerkung
Es ist möglich, die zusätzliche Variable zu eliminieren, um Pop zu einem Betrieb, indem Pop reorganisiertfirstbei jedem Aufruf anstelle von Pushdie ganzen Arbeit.O(n−−√)first