Відповідь нижче - «обман», оскільки, хоча він не використовує проміжку між операціями, самі операції можуть використовувати більше, ніж пробіл . Дивіться деінде в цій темі, щоб відповісти, що не має цієї проблеми.O(1)
Хоча я не маю відповіді на ваше точне запитання, я знайшов алгоритм, який працює в час замість . Я вважаю, що це жорстко, хоча у мене немає доказів. Якщо що-небудь, алгоритм показує, що намагатися довести нижню межу марно, тому це може допомогти у відповіді на ваше запитання.O(n)O(n)O(n−−√)O(n)O(n)
Я представляю два алгоритми, перший - простий алгоритм з часом роботи для Pop, а другий з час роботи для Pop. Я описую перший головним чином через його простоту, так що другий легше зрозуміти.O ( √O(n)O(n−−√)
Щоб детальніше розповісти: перший не використовує додаткового простору, має найгірший випадок (та амортизований) Push та гірший випадок (та амортизований) Pop, але поведінка в гіршому випадку не завжди спрацьовує. Оскільки він не використовує додаткового місця поза двома чергами, це трохи краще, ніж рішення, яке пропонує Росс Снайдер.O ( n )O(1)O(n)
Друге використовує єдине ціле поле (тому додатковий простір), має найгірший випадок (і амортизується) Push та амортизований Pop. Тому час роботи значно кращий, ніж у «простого» підходу, але він використовує додаткове місце.O ( 1 ) O ( √O(1)O(1)O(n−−√)
Перший алгоритм
У нас дві черги: черга та черга s e c o n d . f i r s t буде нашою "чергою наштовхування", тоді як s e c o n d буде чергою, вже в "порядку стеку".firstsecondfirstsecond
- Натискання виконується простим додаванням параметра на .first
- Вискакування робиться наступним чином. Якщо порожній, ми просто відміняємо s e c o n d і повертаємо результат. В іншому випадку ми повернемо f i r s t , додамо всі s e c o n d до f i r s t і поміняємо f i r s t і s e c o n d . Потім ми Dequeue з е з проfirstsecondfirstsecondfirstfirstsecond і повернути результат dequeue.second
C # код першого алгоритму
Це може бути досить читабельним, навіть якщо ви ніколи раніше не бачили C #. Якщо ви не знаєте, що таке дженерики, просто замініть всі екземпляри 'T' на 'string' у вашій думці, щоб створити купу рядків.
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();
}
}
}
Аналіз
Очевидно, що Push працює в час. Поп може торкатися всього, що знаходиться всередині f i r s t і s e c o n d постійну кількість разів, тому у нас є O ( n ) в гіршому випадку. Алгоритм демонструє таку поведінку (наприклад), якщо один натискає на стек n елементів, а потім повторно виконує окрему операцію Push і одну операцію Pop послідовно.O(1)firstsecondO(n)n
Другий алгоритм
У нас дві черги: черга та черга s e c o n d . f i r s t буде нашою "чергою наштовхування", тоді як s e c o n d буде чергою, вже в "порядку стеку".firstsecondfirstsecond
Це адаптована версія першого алгоритму, в якій ми не одразу «перетасовуємо» вміст у s e c o n d . Натомість, якщо f i r s t містить достатньо малу кількість елементів порівняно з s e c o n d (а саме квадратний корінь кількості елементів у s e c o n d ), ми реорганізуємо лише f i r s t в порядок стека і не зливайте його зfirstsecondfirstsecondsecondfirst .second
- Натискання все ще робиться простим додаванням параметра на .first
- Вискакування робиться наступним чином. Якщо порожній, ми просто відміняємо s e c o n d і повертаємо результат. В іншому випадку ми реорганізуємо вміст f i r s t так, щоб вони були в порядку стеку. Якщо | f i r s t | < √firstsecondfirstми просто відмовляємося відfirstі повертаємо результат. В іншому випадку ми додаємоsecondнаfirst, підміняємоfirstіsecond, відміняємоsecondі повертаємо результат.| first|<|second|−−−−−−−√firstsecondfirstfirstseconds e c o n d
C # код першого алгоритму
Це може бути досить читабельним, навіть якщо ви ніколи раніше не бачили C #. Якщо ви не знаєте, що таке дженерики, просто замініть всі екземпляри 'T' на 'string' у вашій думці, щоб створити купу рядків.
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();
}
}
}
}
Аналіз
Очевидно, що Push працює в час.O ( 1 )
Поп працює в амортизований час. Є два випадки: якщо| first| < √О ( н.)--√), тоді ми переміщуємоfirstв порядок стека вO(|first|)=O(√| first|<|second|−−−−−−−√fi r s tчас. Якщо| first| ≥ √О ( | фirst|)=O(n−−√), тоді у нас повинно було бути принаймні√|first|≥|second|−−−−−−−√ дзвінків на Push. Отже, ми можемо вражати цей випадок лише кожен √n−−√ дзвінків на Push and Pop. Фактичний час роботи для цього випадку дорівнюєO(n), тому амортизований час -O( n)n−−√O(n).O(nn√)=O(n−−√)
Заключна примітка
Можна усунути зайву змінну ціною виготовлення Pop a операція, шляхом того, щоб Pop реорганізувавfirstпід час кожного дзвінка, замість того, щоб Push виконати всю роботу.O(n−−√)first