Ответ ниже - «обман», поскольку он не использует пробел между операциями, но сами операции могут использовать больше, чем пробел. Смотрите в другом месте в этой теме ответ, у которого нет этой проблемы.O(1)
Хотя у меня нет ответа на ваш точный вопрос, я нашел алгоритм, который работает в время вместоO(n). Я считаю, что это жестко, хотя у меня нет доказательств. Во всяком случае, алгоритм показывает, что пытаться доказать нижнюю границуO(n)бесполезно, поэтому он может помочь в ответе на ваш вопрос.O(n−−√)O(n)O(n)
Я представляю два алгоритма, первый из которых представляет собой простой алгоритм с временем выполнения для Pop, а второй - с O ( √O(n)время работы для поп. Я описываю первый в основном из-за его простоты, так что второй легче понять.O(n−−√)
Более подробно: первый не использует дополнительного пробела, имеет толчок в худшем случае (и амортизированный) и O ( n ) в худшем случае (и амортизированный), но поведение в худшем случае не всегда срабатывает. Поскольку он не использует никакого дополнительного пространства за пределами двух очередей, он немного «лучше», чем решение, предложенное Россом Снайдером.O(1)O(n)
Второе использует одно целочисленное поле (таким образом, дополнительный пробел), имеет O ( 1 ) худший случай (и амортизируется) Push и O ( √O(1)O(1)амортизированный поп. Поэтому время его выполнения значительно лучше, чем у «простого» подхода, но все же использует дополнительное пространство.O(n−−√)
Первый алгоритм
Мы имеем две очереди: очередь и очереди ы е гр ö н д . е я г с т будет наша «толчок очереди», а с е с о н д будет очередь уже «порядка стека».firstsecondfirstsecond
- Нажатие делаются просто enqueueing параметра на .first
- Поппинг делается следующим образом. Если пусто, мы просто Dequeue с е с о н д и возвращает результат. В противном случае, мы обратные е я R сек т , Append всех ы е гр ö н д к е я R сек т и подкачки е я г с т и с е с о н д . Затем мы Dequeue с е с оfirstsecondfяrstsecondfirstfirstsecond и вернуть результат удаления из очереди.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 работает за раз. Поп может касаться все внутри е я г с т и с е с о п d величины постоянного раза, поэтому мы имеем O ( п ) в худшем случае. Алгоритм демонстрирует это поведение (например), если один помещает n элементов в стек, а затем многократно выполняет одну операцию Push и одну операцию Pop подряд.O ( 1 )еirstsecondO(n)n
Второй алгоритм
Мы имеем две очереди: очередь и очереди ы е гр ö н д . е я г с т будет наша «толчок очереди», а с е с о н д будет очередь уже «порядка стека».firstsecondfirstsecond
Это адаптированная версия первого алгоритма, в котором мы не сразу «перетасовка» содержимое в ы е гр ö н д . Вместо этого, если е я г ы т содержит достаточно малое количество элементов по сравнению с ами е гр ö н д (а именно квадратный корень из числа элементов в ы е с ô п г ), мы только Реорганизовать е я г с т в порядке стека и не сливать его сfirstsecondfirstsecondsecondfirst .second
- Нажатие все еще сделано, просто enqueueing параметра на .first
- Поппинг делается следующим образом. Если пусто, мы просто Dequeue с е с о н д и возвращает результат. В противном случае, мы реорганизовать содержимое е я ¨R сек т так , что они в порядке стека. Если | е я R сек т | < √firstsecondfirstмы просто DEQUEUEFяRсекти возвращает результат. В противном случае, мы AppendсесонднафяRсект, свопеяRытиыегрöнд, DEQUEUEыегрöнди возвращает результат.|first|<|second|−−−−−−−√firstsecondfirstfirstsecondsecond
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)
Поп работает в амортизированное время Есть два случая: если| еяRсект| < √O(n−−√), затем мы перетасовываемfirstв порядок стеков вO(|first|)=O(√|first|<|second|−−−−−−−√firstвремя Если| еяRсект| ≥ √O(|first|)=O(n−−√)тогда у нас должно было быть как минимум√|first|≥|second|−−−−−−−√ призывает толкать. Следовательно, мы можем ударить только в этом случае каждый √n−−√ звонки в Push и Pop. Фактическое время работы для этого случая -O(n), поэтому амортизированное время -O( n).n−−√O(n).O(nn√)=O(n−−√)
Конечная нота
Это можно исключить дополнительную переменную за счет создания Pop операции, при наличии Pop РеорганизоватьфяRсектпри каждом вызове вместо тогоНажмите делать всю работу.O(n−−√)first