คำตอบดังต่อไปนี้คือ 'โกง' ในขณะที่มันไม่ได้ใช้พื้นที่ใด ๆ ระหว่างการดำเนินการการดำเนินงานที่ตัวเองสามารถใช้มากกว่าพื้นที่ ดูที่อื่นในหัวข้อนี้สำหรับคำตอบที่ไม่มีปัญหานี้O ( 1 )
ในขณะที่ฉันไม่มีคำตอบสำหรับคำถามที่แน่นอนของคุณ แต่ฉันพบอัลกอริทึมที่ทำงานในเวลาแทนO(n) ฉันเชื่อว่านี่มันแน่น แต่ฉันไม่มีหลักฐาน หากมีสิ่งใดอัลกอริทึมแสดงให้เห็นว่าการพยายามพิสูจน์ขอบเขตล่างของO(n)นั้นไร้ประโยชน์ดังนั้นจึงอาจช่วยตอบคำถามของคุณได้O ( n--√)O ( n )O ( n )
ฉันนำเสนออัลกอริทึมสองอย่างสิ่งแรกคืออัลกอริธึมแบบง่ายโดยใช้เวลาสำหรับป๊อปและที่สองด้วยO ( √)O ( n )เวลาที่ใช้สำหรับป๊อป ฉันอธิบายคนแรกส่วนใหญ่เป็นเพราะความเรียบง่ายเพื่อให้คนที่สองง่ายต่อการเข้าใจO ( n--√)
หากต้องการให้รายละเอียดเพิ่มเติม: ครั้งแรกที่ไม่ใช้พื้นที่เพิ่มเติมมีกรณีที่เลวร้ายที่สุด (และตัดจำหน่าย) Push และO ( n )กรณีที่เลวร้ายที่สุด (และตัดจำหน่าย) ป๊อป แต่พฤติกรรมกรณีที่เลวร้ายที่สุดไม่ได้เรียกเสมอ เนื่องจากไม่ได้ใช้พื้นที่เพิ่มเติมนอกเหนือจากสองคิวจึง 'ดีกว่า' เล็กน้อยกว่าโซลูชันที่ Ross Snider นำเสนอO ( 1 )O ( n )
ที่สองใช้เขตข้อมูลจำนวนเต็มเดียว (ดังนั้นพื้นที่พิเศษ ), มีO ( 1 )กรณีที่เลวร้ายที่สุด (และตัดจำหน่าย) ผลักดันและO ( √)O ( 1 )O ( 1 )ป๊อปตัดจำหน่าย เวลาทำงานจึงดีกว่าวิธีการ 'เรียบง่าย' อย่างมาก แต่ก็ใช้พื้นที่เพิ่มเติมบางส่วนO ( n--√)
อัลกอริทึมแรก
เรามีสองคิว: คิวและคิวs อีคo n d ฉฉันr s Tจะผลักดันคิว 'ของเราในขณะที่อีคo n dจะเป็นคิวที่มีอยู่แล้วในการสั่งซื้อสแต็ค'ฉฉันr s Ts e c o n dฉฉันr s Ts e c o n d
- ผลักดันที่จะทำโดยเพียงแค่ enqueueing พารามิเตอร์บน Tฉฉันr s T
- Popping ทำได้ดังนี้ หากเป็นที่ว่างเปล่าเราก็ dequeue s อีคo n dและกลับผล มิฉะนั้นเราจะย้อนกลับฉฉันr s Tผนวกทั้งหมดของs อีคo n dจะฉฉันr s Tและสลับฉฉันr s Tและs อีคo n d จากนั้นเราจะ dequeue s อีคoฉฉันr s Ts e c o n dฉฉันr s Ts e c o n dฉฉันr s Tฉฉันr s Ts e c o n dและส่งคืนผลลัพธ์ของ dequeues e c o n d
รหัส C # สำหรับอัลกอริทึมแรก
สิ่งนี้ควรอ่านได้แม้ว่าคุณจะไม่เคยเห็น C # มาก่อนก็ตาม หากคุณไม่ทราบว่าชื่อสามัญคืออะไรให้แทนที่ 'สตริง' ด้วย 'สตริง' ทั้งหมดในใจของคุณเพื่อเป็นชุดของสตริง
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();
}
}
}
การวิเคราะห์
เห็นได้ชัดว่าผลักดันงานในเวลา ป๊อปอาจจะสัมผัสกับทุกอย่างภายในฉฉันr s Tและs อีคo n dเป็นจำนวนเงินที่คงที่ของเวลาดังนั้นเราจึงมีO ( n )ในกรณีที่เลวร้ายที่สุด ขั้นตอนวิธีการแสดงพฤติกรรมนี้ (ตัวอย่างเช่น) หากผลักดันnองค์ประกอบบนสแต็คแล้วซ้ำแล้วซ้ำอีกจะดำเนินการผลักดันไหม้และการดำเนินงานที่ป๊อปเดียวในการสืบทอดO ( 1 )ฉฉันr s Ts e c o n dO(n)n
อัลกอริทึมที่สอง
เรามีสองคิว: คิวและคิวs อีคo n d ฉฉันr s Tจะผลักดันคิว 'ของเราในขณะที่อีคo n dจะเป็นคิวที่มีอยู่แล้วในการสั่งซื้อสแต็ค'firstsecondfirstsecond
นี้เป็นรุ่นที่ดัดแปลงของขั้นตอนวิธีแรกที่เราทำไม่ได้ทันที 'สับ' เนื้อหาของเข้าไปs อีคo n d แต่ถ้าฉฉันr s ทีมีขนาดเล็กจำนวนเพียงพอขององค์ประกอบเมื่อเทียบกับs อีคo n d (คือรากที่สองของจำนวนขององค์ประกอบในs อีคo n d ) เราเพียงจัดระเบียบฉฉันr s Tในลำดับสแต็กและไม่รวมเข้าด้วยfirsts e c o n dฉฉันr s Tsecondsecondฉฉันr s T dsecond
- ผลักดันจะทำยังโดยเพียงแค่ enqueueing พารามิเตอร์บน Tfirst
- Popping ทำได้ดังนี้ หากเป็นที่ว่างเปล่าเราก็ dequeue s อีคo n dและกลับผล มิฉะนั้นเราจัดระเบียบเนื้อหาของฉฉันr s Tเพื่อให้พวกเขาอยู่ในลำดับที่สแต็ค ถ้า| f i r s t | < √firstsecondfirstเราก็ dequeueฉฉันrsTและส่งกลับผล มิฉะนั้นเราผนวกsอีคondบนฉฉันrsTสลับฉฉันrsTและsอีคond, dequeuesอีคondและกลับผล|first|<|second|−−−−−−−√ฉฉันr s Ts e c o n dฉฉันr s Tฉฉันr s Ts e c o n ds e c o n d
รหัส C # สำหรับอัลกอริทึมแรก
สิ่งนี้ควรอ่านได้แม้ว่าคุณจะไม่เคยเห็น C # มาก่อนก็ตาม หากคุณไม่ทราบว่าชื่อสามัญคืออะไรให้แทนที่ 'สตริง' ด้วย 'สตริง' ทั้งหมดในใจของคุณเพื่อเป็นชุดของสตริง
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();
}
}
}
}
การวิเคราะห์
เห็นได้ชัดว่าผลักดันงานในเวลาO ( 1 )
ป๊อปทำงานในเวลาตัดจำหน่าย มีสองกรณี: ถ้า| first| < √O ( n--√)แล้วเราสับเปลี่ยนฉฉันrsTเป็นใบสั่งสแต็คในO(|ฉฉันrsT|)=O(√| ฉฉันr s T | < | s e c o n d|-------√ฉฉันr s Tเวลา ถ้า| first| ≥ √O ( | fฉันr s T | ) = O ( n--√)แล้วเราจะต้องมีอย่างน้อย√| ฉฉันr s T | ≥ | s e c o n d|-------√เรียกร้องให้กด ดังนั้นเราสามารถตีคดีนี้ได้ทุก √n--√โทรไปยัง Push and Pop เวลาทำงานจริงสำหรับกรณีนี้คือO(n)ดังนั้นเวลาที่ตัดจำหน่ายคือO( n)n--√O ( n ) )O ( nn√) = O ( n--√)
หมายเหตุสุดท้าย
มันเป็นไปได้ที่จะกำจัดตัวแปรพิเศษด้วยค่าใช้จ่ายในการสร้าง Pop an การดำเนินงานโดยมีป๊อปจัดระเบียบฉฉันrsTทวงถามแทนที่จะต้องกดทุกทำทุกงานO ( n--√)ฉฉันr s T