以下の答えは「不正行為」です。オペレーション間でスペースを使用しませんが、オペレーション自体は以上のスペースを使用できます。この問題のない答えについては、このスレッドの他の場所を参照してください。O (1 )
あなたの正確な質問に対する答えはありませんが、Oで機能するアルゴリズムを見つけました(√O(n)の代わりの時間。証拠はありませんが、これはきついと思います。どちらかといえば、アルゴリズムはO(n)の下限を証明しようとすることは無益であることを示しているので、あなたの質問に答えるのに役立つかもしれません。O (n−−√)O (n )O (n )
私は2つのアルゴリズムを提示します。1つはPopの実行時間を持つ単純なアルゴリズムで、2つ目はO (√O (n )ポップの実行時間。2番目の方がわかりやすいように、主にその単純さのために最初の方を説明します。O (n−−√)
詳細を説明すると、最初は追加のスペースを使用せず、最悪ケース(および償却)プッシュとO (n )最悪ケース(および償却)ポップがありますが、最悪の場合の動作は常にトリガーされません。2つのキューを超える追加スペースを使用しないため、Ross Sniderが提供するソリューションよりもわずかに「優れています」。O (1 )O (n )
2番目は単一の整数フィールドを使用するため(余分なスペース)、O (1 )ワーストケース(および償却)プッシュとO (√O (1 )O (1 )償却されたポップ。したがって、実行時間は「単純な」アプローチよりもはるかに優れていますが、余分なスペースを使用します。O (n−−√)
最初のアルゴリズム
キューとキューs e c o n dの 2つのキューがあります。f i r s tは「プッシュキュー」になり、s e c o n dはすでに「スタック順」にあるキューになります。f私は、rはsのトンS E C 、O 、N Df私は、rはsのトンS E C 、O 、N D
- プッシュは、単にパラメーターをエンキューすることで行われます。f私は、rはsのトン
- ポッピングは次のように行われます。もし空である、我々は単にデキュー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。次に、s e c oをデキューしますf私は、rはsのトンS E C 、O 、N Df私は、rはsのトンS E C 、O 、N Df私は、rはsのトンf私は、rはsのトンS E C 、O 、N Dおよびデキューの結果を返します。S 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>();
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は時間で動作します。Popは、f i r s tおよびs e c o n d内のすべてに一定の回数接触する可能性があるため、最悪の場合にはO (n )があります。アルゴリズムは、n個の要素をスタックにプッシュしてから、単一のプッシュ操作と単一のポップ操作を連続して繰り返し実行すると、この動作を示します(たとえば)。O (1 )f私は、rはsのトンS E C 、O 、N DO (n )n
2番目のアルゴリズム
キューとキューs e c o n dの 2つのキューがあります。f i r s tは「プッシュキュー」になり、s e c o n dはすでに「スタック順」にあるキューになります。f私は、rはsのトンS E C 、O 、N Df私は、rはsのトンS E C 、O 、N D
これは、最初のアルゴリズムの適応バージョンであり、内容をすぐに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をスタック順に並べ、マージしないf私は、rはsのトンS E C 、O 、N Df私は、rはsのトンS E C 、O 、N DS E C 、O 、N Df私は、rはsのトン。S E C 、O 、N D
- プッシュは、単にパラメーターをエンキューするだけで行われます。f私は、rはsのトン
- ポッピングは次のように行われます。もし空である、我々は単にデキューS E C 、O 、N 、D、結果を返します。それ以外の場合、f i r s tの内容を再編成して、スタック順になります。もし| f i r s t | < √f私は、rはsのトンS E C 、O 、N Df私は、rはsのトンfirstを単にデキューし、結果を返します。そうでなければ、我々は、追加のSEのCの入出力のnDへのFiはrのSTは、スワップfはiはrのSTとSのE部のC、O、NDデキュー、S、E、C、O、N個のD及び結果を返すを。| f私は、rはsのトン| < | S E C 、O 、N D|−−−−−−−√f私rはsのトンS E C 、O 、N Df私は、rはsのトンf私は、rはsのトンS E C 、O 、N DS 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 )
ポップはOで動作します(√償却時間。次の2つの場合があります。first| < √O (n−−√)、その後、firstをO(|first|)=O(√| fi r s t | < | S E C 、O 、N D|−−−−−−−√f私は、rはsのトン時間。もし| first| ≥ √O (| fi r s t | )= O (n−−√)、その後、少なくとも√| fi r s t | ≥ | S E C 、O 、N D|−−−−−−−√はプッシュを要求します。したがって、このケースはすべて √n−−√プッシュとポップを呼び出します。この場合の実際の実行時間はO(n)なので、償却時間はO( nn−−√O (n )。O (nn√)= O (n−−√)
最後のメモ
PopをOにすることで余分な変数を削除することが可能です(√操作。Pushにすべての作業を行わせる代わりに、呼び出しごとにfirstを再編成させます。O (n−−√)f私は、rはsのトン