Odpowiedź poniżej brzmi „oszustwo”, ponieważ chociaż nie używa on żadnej przestrzeni między operacjami, same operacje mogą wykorzystywać więcej niż przestrzeń . Zobacz gdzie indziej w tym wątku, aby uzyskać odpowiedź, w której nie ma tego problemu.O ( 1 )
Chociaż nie mam odpowiedzi na dokładne pytanie, znalazłem algorytm, który działa w czas zamiastO(n). Uważam, że jest ciasno, chociaż nie mam dowodu. Jeśli już, algorytm pokazuje, że próba udowodnienia dolnej granicyO(n)jest daremna, więc może pomóc w odpowiedzi na twoje pytanie.O ( n--√)O ( n )O ( n )
Przedstawiam dwa algorytmy, pierwszy z nich to prosty algorytm z czasem działania dla Pop, a drugi z O ( √O ( n )czas działania Pop. Pierwszy opisuję głównie ze względu na jego prostotę, dzięki czemu drugi jest łatwiejszy do zrozumienia.O ( n--√)
Aby podać więcej szczegółów: pierwsze nie używa dodatkowej spacji, ma najgorszy przypadek (i amortyzowane) Push i najgorszy przypadek O ( n ) (i amortyzowane) Pop, ale zachowanie najgorszego przypadku nie zawsze jest wyzwalane. Ponieważ nie wykorzystuje żadnej dodatkowej przestrzeni poza dwiema kolejkami, jest nieco „lepszy” niż rozwiązanie oferowane przez Rossa Snidera.O ( 1 )O ( n )
Drugi używa pojedynczego pola liczb całkowitych (więc dodatkowa spacja ), ma najgorszy przypadek O ( 1 ) (i zamortyzowany) Push i O ( √O ( 1 )O ( 1 )amortyzowany Pop. Czas jego działania jest zatem znacznie lepszy niż w przypadku „prostego” podejścia, ale wymaga on dodatkowej przestrzeni.O ( n--√)
Pierwszy algorytm
Mamy dwie kolejki: kolejkę kolejek s e c o n D . f i r s t będzie naszą „kolejką wypychającą”, podczas gdy s e c o n d będzie kolejką już w „kolejności stosów”.fairstsecondfirstsecond
- Popychanie odbywa się po prostu przez skolejkowania parametru na .first
- Wyskakiwanie odbywa się w następujący sposób. Jeśli jest pusty, po prostu rozkolejkowania a e C O n d i zwraca wynik. W przeciwnym razie odwracamy f i r s t , dołączamy wszystkie s e c o n d do f i r s t i zamieniamy f i r s t i s e c o n d . Następnie dequeue s e c ofirstsecondfirsts e c o n dfai r s tfai r s ts ec o n d i zwróć wynik dequeue.s ec o n d
Kod C # dla pierwszego algorytmu
Może to być dość czytelne, nawet jeśli nigdy wcześniej nie widziałeś C #. Jeśli nie wiesz, jakie są ogólne, po prostu zamień wszystkie wystąpienia „T” na „ciąg” w swoim umyśle, aby uzyskać stos ciągów.
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();
}
}
}
Analiza
Oczywiście Push działa w czasie . Pop może dotykać wszystko wewnątrz F I r s t a a e C O n d stałej ilości czasu, więc mamy O ( n ) w najgorszym przypadku. Algorytm wykazuje takie zachowanie (na przykład), jeśli popycha się n elementów na stos, a następnie wielokrotnie wykonuje pojedynczą operację Push i pojedynczą operację Pop.O ( 1 )fajar s ts e co n dO( n )n
Drugi algorytm
Mamy dwie kolejki: kolejkę kolejek s e c o n D . f i r s t będzie naszą „kolejką wypychającą”, podczas gdy s e c o n d będzie kolejką już w „kolejności stosów”.fai r sts e c o ndfai r sts e c o nd
Jest to dostosowane wersję pierwszego algorytmu, w którym nie od razu SHUFFLE ", których zawartość w a e C O n d . Zamiast tego, jeśli f i r s t zawiera wystarczająco małą liczbę elementów w porównaniu do s e c o n d (mianowicie pierwiastek kwadratowy z liczby elementów w s e c o n d ), reorganizujemy tylko f i r s t w stosie i nie łącz gofai r s ts e c o n dfai r s ts e c o n ds e c o n dfai r s t .s e c o n d
- Popychanie nadal odbywa się po prostu przez skolejkowania parametru na .fai r s t
- Wyskakiwanie odbywa się w następujący sposób. Jeśli jest pusty, po prostu rozkolejkowania a e C O n d i zwraca wynik. Inaczej, porządkowanie zawartości F I r s t , tak, że są w kolejności stosu. Jeśli | f i r s t | < √fai r s ts e c o n dfai r s tpo prostu rozkolejkowaniaFIrsti zwraca wynik. W przeciwnym razie dodaćeeconDnafirst, swapFIrstaaeCo,nd, rozkolejkowaniaeeco,nDi zwraca wynik.| fairst|<|second|−−−−−−−√firstsecondfirstfirstsecondsecond
Kod C # dla pierwszego algorytmu
Może to być dość czytelne, nawet jeśli nigdy wcześniej nie widziałeś C #. Jeśli nie wiesz, jakie są ogólne, po prostu zamień wszystkie wystąpienia „T” na „ciąg” w swoim umyśle, aby uzyskać stos ciągów.
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();
}
}
}
}
Analiza
Oczywiście Push działa w czasie .O(1)
Pop działa w amortyzowany czas. Istnieją dwa przypadki: jeśli| first| < √O(n−−√), a następnie tasujemyfirstw kolejności stosu wO(|first|)=O(√|first|<|second|−−−−−−−√firstczas. Jeśli| first| ≥ √O(|first|)=O(n−−√), to musieliśmy mieć przynajmniej√|first|≥|second|−−−−−−−√ wzywa do Push. Stąd możemy tylko hit tego sprawę każdy √n−−√ połączeń z Push i Pop. Rzeczywisty czas działania dla tego przypadku wynosiO(n), więc zamortyzowany czas wynosiO( nn−−√O(n).O(nn√)=O(n−−√)
Ostatnia uwaga
Możliwe jest wyeliminowanie dodatkowej zmiennej kosztem zmiany Pop na operacji, poprzez reorganizację popFIrstw każdym połączeniu zamiast push wszystkie prace.O(n−−√)first