Jawaban di bawah ini adalah 'curang', karena walaupun tidak menggunakan ruang antara operasi, operasi itu sendiri dapat menggunakan lebih dari ruang. Lihat tempat lain di utas ini untuk jawaban yang tidak memiliki masalah ini.O(1)
Meskipun saya tidak punya jawaban untuk pertanyaan Anda, saya menemukan sebuah algoritma yang berfungsi di waktu alih-alihO(n). Saya percaya ini ketat, meskipun saya tidak punya bukti. Jika ada, algoritme menunjukkan bahwa mencoba membuktikan batas bawahO(n)adalah sia-sia, sehingga mungkin membantu dalam menjawab pertanyaan Anda.O(n−−√)O(n)O(n)
Saya menyajikan dua algoritma, yang pertama adalah algoritma sederhana dengan waktu berjalan untuk Pop dan yang kedua dengan O ( √O(n)waktu berjalan untuk Pop. Saya menggambarkan yang pertama terutama karena kesederhanaannya sehingga yang kedua lebih mudah dimengerti.O(n−−√)
Untuk memberikan rincian lebih lanjut: yang pertama tidak menggunakan ruang tambahan, memiliki kasus terburuk (dan diamortisasi) Push dan O ( n ) kasus terburuk (dan diamortisasi) Pop, tetapi perilaku kasus terburuk tidak selalu dipicu. Karena tidak menggunakan ruang tambahan di luar dua antrian, itu sedikit 'lebih baik' daripada solusi yang ditawarkan oleh Ross Snider.O(1)O(n)
Yang kedua menggunakan bidang bilangan bulat tunggal (jadi ruang ekstra), memiliki O ( 1 ) kasus terburuk (dan diamortisasi) Push dan O ( √O(1)O(1)Pop diamortisasi. Karenanya waktu berjalan jauh lebih baik daripada pendekatan 'sederhana', namun ia menggunakan ruang ekstra.O(n−−√)
Algoritma pertama
Kami memiliki dua antrian: antrian dan antrian s e c o n d . f i r s t akan menjadi 'push queue' kami, sementara s e c o n d akan menjadi antrian yang sudah ada di 'susun urutan'.firstsecondfirstsecond
- Mendorong dilakukan dengan hanya memberikan parameter ke .first
- Popping dilakukan sebagai berikut. Jika kosong, kita hanya dequeue s e c o n d dan mengembalikan hasil. Kalau tidak, kita membalikkan f i r s t , menambahkan semua s e c o n d ke f i r s t dan bertukar f i r s t dan s e c o n d . Kami kemudian dequeue s e c ofirstsecondfirstsecondfirstfirstsecond dan mengembalikan hasil dari dequeue tersebut.second
Kode C # untuk algoritma pertama
Ini seharusnya cukup mudah dibaca, bahkan jika Anda belum pernah melihat C # sebelumnya. Jika Anda tidak tahu apa itu generik, cukup ganti semua contoh 'T' dengan 'string' di pikiran Anda, untuk setumpuk 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();
}
}
}
Analisis
Jelas Push bekerja dalam waktu. Pop dapat menyentuh segala sesuatu di dalam f i r s t dan s e c o n d jumlah konstan kali, jadi kita harus O ( n ) dalam kasus terburuk. Algoritme menunjukkan perilaku ini (misalnya) jika seseorang mendorong n elemen ke stack dan kemudian berulang kali melakukan Push menghanguskan dan operasi Pop tunggal berturut-turut.O(1)firstsecondO(n)n
Algoritma kedua
Kami memiliki dua antrian: antrian dan antrian s e c o n d . f i r s t akan menjadi 'push queue' kami, sementara s e c o n d akan menjadi antrian yang sudah ada di 'susun urutan'.firstsecondfirstsecond
Ini adalah versi adaptasi dari algoritma pertama, di mana kita tidak langsung 'mengacak' isi dari menjadi s e c o n dfirstsecond . Sebaliknya, jika berisi sejumlah cukup kecil elemen dibandingkan dengan s e c o n d (yaitu akar kuadrat dari jumlah elemen di s e c o n d ), kita hanya menata f i r s t ke dalam susunan tumpukan dan jangan bergabung denganfirstsecondsecondfirst .second
- Mendorong masih dilakukan dengan hanya memberikan parameter ke .first
- Popping dilakukan sebagai berikut. Jika first kosong, kita hanya dequeue dan mengembalikan hasil. Jika tidak, kami reorganisasi isi f i r s t sehingga mereka berada di urutan stack. Jika | f i r s t | < √secondfirstkita hanya dequeuefirstdan mengembalikan hasilnya. Kalau tidak, kita menambahkansecondkefirst, bertukarfirstdansecond, dequeueseconddan mengembalikan hasilnya.|first|<|second|−−−−−−−√firstsecondfirstfirstsecondsecond
Kode C # untuk algoritma pertama
Ini seharusnya cukup mudah dibaca, bahkan jika Anda belum pernah melihat C # sebelumnya. Jika Anda tidak tahu apa itu generik, cukup ganti semua contoh 'T' dengan 'string' di pikiran Anda, untuk setumpuk 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();
}
}
}
}
Analisis
Jelas Push bekerja dalam waktu.O(1)
Pop bekerja di waktu diamortisasi. Ada dua kasus: jika| first| < √O(n−−√)|first|<|second|−−−−−−−√, Maka kita mengocok ke dalam urutan tumpukan di O ( | f i r s t | ) = O ( √firstwaktu. Jika| first| ≥ √O(|first|)=O(n−−√), maka kita harus memiliki setidaknya√|first|≥|second|−−−−−−−√ panggilan untuk Push. Karenanya, kami hanya dapat menemukan kasus ini setiap √n−−√ panggilan ke Push dan Pop. Waktu berjalan aktual untuk kasus ini adalahO(n), jadi waktu diamortisasi adalahO( nn−−√O(n).O(nn√)=O(n−−√)
Catatan akhir
Dimungkinkan untuk menghilangkan variabel tambahan dengan biaya membuat Pop an operasi, dengan memiliki Pop mereorganisasifirstdi setiap panggilan daripada harus push melakukan semua pekerjaan.O(n−−√)first