สมมติว่าเรามีสองกองและไม่มีตัวแปรชั่วคราวอื่น ๆ
เป็นไปได้ที่จะ "สร้าง" โครงสร้างข้อมูลคิวโดยใช้ทั้งสองกองเท่านั้นหรือไม่
สมมติว่าเรามีสองกองและไม่มีตัวแปรชั่วคราวอื่น ๆ
เป็นไปได้ที่จะ "สร้าง" โครงสร้างข้อมูลคิวโดยใช้ทั้งสองกองเท่านั้นหรือไม่
คำตอบ:
เก็บ 2 กองขอเรียกพวกเขาและinbox
outbox
เข้าคิว :
inbox
Dequeue :
หากoutbox
ไม่มีข้อมูลให้เติมโดยการเปิดแต่ละองค์ประกอบจากinbox
นั้นกดลงoutbox
ป๊อปและกลับองค์ประกอบยอดนิยมจาก outbox
การใช้วิธีนี้แต่ละองค์ประกอบจะอยู่ในแต่ละสแต็คหนึ่งครั้ง - หมายถึงแต่ละองค์ประกอบจะถูกผลักสองครั้งและผุดสองครั้งทำให้มีการดำเนินการเวลาคงที่ที่ถูกตัดจำหน่าย
นี่คือการใช้งานใน Java:
public class Queue<E>
{
private Stack<E> inbox = new Stack<E>();
private Stack<E> outbox = new Stack<E>();
public void queue(E item) {
inbox.push(item);
}
public E dequeue() {
if (outbox.isEmpty()) {
while (!inbox.isEmpty()) {
outbox.push(inbox.pop());
}
}
return outbox.pop();
}
}
เพื่อให้เข้าใจถึงวิธีการสร้างคิวโดยใช้สองสแต็คคุณควรเข้าใจวิธีการกลับสแต็คคริสตัลที่ชัดเจน โปรดจำไว้ว่าการทำงานของสแต็กคล้ายกับจานสแต็คในครัวของคุณมาก จานล้างสุดท้ายจะอยู่ด้านบนของกองสะอาดซึ่งเรียกว่าเป็นL ast I n F irst O ut (LIFO) ในวิทยาศาสตร์คอมพิวเตอร์
ให้จินตนาการว่ากองของเราเหมือนขวดดังต่อไปนี้
หากเรากดจำนวนเต็ม 1,2,3 ตามลำดับแล้ว 3 จะอยู่ด้านบนสุดของสแต็ก เนื่องจาก 1 จะถูกผลักก่อนจากนั้นจะวาง 2 ที่ด้านบนของ 1 สุดท้าย 3 จะวางที่ด้านบนสุดของสแต็คและสถานะล่าสุดของสแต็กของเราที่แสดงเป็นขวดจะเป็นด้านล่าง
ตอนนี้เรามีสแต็กของเราแสดงเป็นขวดที่บรรจุด้วยค่า 3,2,1 และเราต้องการย้อนกลับสแต็คเพื่อให้องค์ประกอบด้านบนของสแต็กจะเป็น 1 และองค์ประกอบด้านล่างของสแต็กจะเป็น 3 เราจะทำอย่างไร เราสามารถนำขวดไปวางไว้คว่ำเพื่อให้ค่าทั้งหมดควรย้อนกลับตามลำดับ
ใช่เราทำได้ แต่นั่นคือขวด ในการทำกระบวนการเดียวกันเราต้องมีสแต็กที่สองซึ่งจะเก็บองค์ประกอบสแต็กแรกในลำดับย้อนกลับ ลองใส่สแต็คที่มีประชากรของเราไปทางซ้ายและสแต็กเปล่าใหม่ของเราไปทางขวา ในการย้อนกลับลำดับขององค์ประกอบเราจะป๊อปแต่ละองค์ประกอบจากสแต็กซ้ายและผลักไปที่สแต็กขวา คุณสามารถเห็นสิ่งที่เกิดขึ้นในขณะที่เราทำในภาพด้านล่าง;
ดังนั้นเราจึงรู้วิธีย้อนกลับกองซ้อน
ในส่วนก่อนหน้านี้ฉันได้อธิบายวิธีที่เราสามารถย้อนกลับลำดับขององค์ประกอบสแต็ค นี่เป็นสิ่งสำคัญเพราะถ้าเรากดและองค์ประกอบป๊อปอัพไปยังสแต็คผลลัพธ์จะอยู่ในลำดับที่แน่นอนของคิว ลองนึกถึงตัวอย่างลองผลักอาร์เรย์จำนวนเต็ม{1, 2, 3, 4, 5}
ไปที่สแต็ก หากเราปรากฏองค์ประกอบและพิมพ์จนกว่าสแต็กจะว่างเปล่าเราจะได้รับอาเรย์ในลำดับย้อนกลับของการผลักดันคำสั่งซึ่งจะ{5, 4, 3, 2, 1}
จำไว้ว่าสำหรับการป้อนข้อมูลเดียวกันถ้าเรา dequeue คิวจนกว่าคิวจะว่างเปล่าเอาท์พุท {1, 2, 3, 4, 5}
จะเป็น ดังนั้นจึงเห็นได้ชัดว่าสำหรับลำดับขององค์ประกอบอินพุตเดียวกันเอาต์พุตของคิวนั้นตรงกันข้ามกับเอาต์พุตของสแต็ก เมื่อเรารู้วิธีย้อนกลับสแต็กโดยใช้สแต็กพิเศษเราสามารถสร้างคิวโดยใช้สองสแต็ค
แบบจำลองคิวของเราจะประกอบด้วยสองกอง สแต็กหนึ่งจะใช้สำหรับenqueue
การดำเนินการ (สแต็ค # 1 ทางด้านซ้ายจะถูกเรียกว่าอินพุตสแต็ก) สแต็กอื่นจะถูกใช้สำหรับการdequeue
ดำเนินการ (สแต็ก # 2 ทางด้านขวาจะถูกเรียกว่าเป็น ตรวจสอบภาพด้านล่าง;
โค้ดหลอกของเรามีดังนี้;
Push every input element to the Input Stack
If ( Output Stack is Empty)
pop every element in the Input Stack
and push them to the Output Stack until Input Stack is Empty
pop from Output Stack
ลองจัดคิวจำนวนเต็ม{1, 2, 3}
ตามลำดับ จำนวนเต็มจะถูกส่งไปยังInput Stack ( Stack # 1 ) ซึ่งอยู่ทางซ้าย
ถ้าเช่นนั้นจะเกิดอะไรขึ้นถ้าเราทำการ dequeue? เมื่อใดก็ตามที่การดำเนินการ dequeue ถูกดำเนินการคิวจะตรวจสอบว่า Output Stack ว่างเปล่าหรือไม่ (ดูรหัสเทียมด้านบน) หาก Output Stack ว่างเปล่าแล้ว Stack Stack จะถูกแยกออกจากเอาต์พุตเพื่อให้องค์ประกอบ ของ Input Stack จะถูกกลับรายการ ก่อนที่จะส่งคืนค่าสถานะของคิวจะเป็นดังนี้
ตรวจสอบลำดับขององค์ประกอบใน Output Stack (Stack # 2) เห็นได้ชัดว่าเราสามารถป๊อปอัพองค์ประกอบจาก Output Stack เพื่อให้ผลลัพธ์จะเหมือนกับว่าเรา dequeued จากคิว ดังนั้นถ้าเราดำเนินการถอนสองครั้งแรกเราจะได้รับ{1, 2}
ตามลำดับ จากนั้นองค์ประกอบ 3 จะเป็นองค์ประกอบเดียวของ Output Stack และ Input Stack จะว่างเปล่า หากเราจัดคิวองค์ประกอบ 4 และ 5 สถานะของคิวจะเป็นดังนี้
ตอนนี้ Output Stack ไม่ว่างเปล่าและถ้าเราทำการ dequeue จะมีเพียง 3 อันที่โผล่ออกมาจาก Output Stack จากนั้นรัฐจะถูกมองว่าเป็นด้านล่าง;
อีกครั้งถ้าเราดำเนินการถอนสองครั้งเพิ่มเติมในการดำเนินการถอนคิวครั้งแรกคิวจะตรวจสอบว่า Output Stack ว่างเปล่าซึ่งเป็นจริง จากนั้นป๊อปอัพอิลิเมนต์ของอินพุตสแต็กและดันไปที่เอ้าท์พุทสแต็คเพราะกองที่ว่างเปล่าจากนั้นสถานะของคิวจะเป็นดังนี้
ง่ายต่อการมองเห็นผลลัพธ์ของการดำเนินการสองคิวจะเป็น {4, 5}
นี่คือการดำเนินการใน Java ฉันจะไม่ใช้การใช้งานของกองซ้อนที่มีอยู่แล้วดังนั้นตัวอย่างที่นี่จะสร้างล้อใหม่
public class MyStack<T> {
// inner generic Node class
private class Node<T> {
T data;
Node<T> next;
public Node(T data) {
this.data = data;
}
}
private Node<T> head;
private int size;
public void push(T e) {
Node<T> newElem = new Node(e);
if(head == null) {
head = newElem;
} else {
newElem.next = head;
head = newElem; // new elem on the top of the stack
}
size++;
}
public T pop() {
if(head == null)
return null;
T elem = head.data;
head = head.next; // top of the stack is head.next
size--;
return elem;
}
public int size() {
return size;
}
public boolean isEmpty() {
return size == 0;
}
public void printStack() {
System.out.print("Stack: ");
if(size == 0)
System.out.print("Empty !");
else
for(Node<T> temp = head; temp != null; temp = temp.next)
System.out.printf("%s ", temp.data);
System.out.printf("\n");
}
}
public class MyQueue<T> {
private MyStack<T> inputStack; // for enqueue
private MyStack<T> outputStack; // for dequeue
private int size;
public MyQueue() {
inputStack = new MyStack<>();
outputStack = new MyStack<>();
}
public void enqueue(T e) {
inputStack.push(e);
size++;
}
public T dequeue() {
// fill out all the Input if output stack is empty
if(outputStack.isEmpty())
while(!inputStack.isEmpty())
outputStack.push(inputStack.pop());
T temp = null;
if(!outputStack.isEmpty()) {
temp = outputStack.pop();
size--;
}
return temp;
}
public int size() {
return size;
}
public boolean isEmpty() {
return size == 0;
}
}
public class TestMyQueue {
public static void main(String[] args) {
MyQueue<Integer> queue = new MyQueue<>();
// enqueue integers 1..3
for(int i = 1; i <= 3; i++)
queue.enqueue(i);
// execute 2 dequeue operations
for(int i = 0; i < 2; i++)
System.out.println("Dequeued: " + queue.dequeue());
// enqueue integers 4..5
for(int i = 4; i <= 5; i++)
queue.enqueue(i);
// dequeue the rest
while(!queue.isEmpty())
System.out.println("Dequeued: " + queue.dequeue());
}
}
Dequeued: 1
Dequeued: 2
Dequeued: 3
Dequeued: 4
Dequeued: 5
คุณสามารถจำลองคิวโดยใช้เพียงหนึ่งสแต็ก สแต็ก (ชั่วคราว) ที่สองสามารถจำลองได้โดยสแต็คการเรียกของการเรียกซ้ำไปยังวิธีการแทรก
หลักการยังคงเหมือนเดิมเมื่อแทรกองค์ประกอบใหม่ลงในคิว:
คลาส Queue ที่ใช้เพียงหนึ่ง Stack จะเป็นดังนี้:
public class SimulatedQueue<E> {
private java.util.Stack<E> stack = new java.util.Stack<E>();
public void insert(E elem) {
if (!stack.empty()) {
E topElem = stack.pop();
insert(elem);
stack.push(topElem);
}
else
stack.push(elem);
}
public E remove() {
return stack.pop();
}
}
n items
ในคิวโดยใช้โครงสร้างข้อมูลข้างต้น รวมผลในการ(1 + 2 + 4 + 8 + .... + 2(n-1))
~O(n^2)
ฉันหวังว่าคุณจะได้รับจุด
ความซับซ้อนของเวลาจะยิ่งแย่ลง การใช้คิวที่ดีทำทุกอย่างในเวลาคงที่
แก้ไข
ไม่แน่ใจว่าทำไมคำตอบของฉันถูกลงที่นี่ หากเราโปรแกรมเราใส่ใจกับความซับซ้อนของเวลาและการใช้สแต็คมาตรฐานสองแบบเพื่อสร้างคิวนั้นไม่มีประสิทธิภาพ มันเป็นจุดที่ถูกต้องและเกี่ยวข้องมาก ถ้ามีคนอื่นรู้สึกว่าต้องลงคะแนนมากกว่านี้ฉันก็อยากจะรู้ว่าทำไม
รายละเอียดเพิ่มเติมเล็กน้อย : ทำไมการใช้สองสแต็คนั้นแย่กว่าคิว: ถ้าคุณใช้สองกองและมีคนโทร dequeue ในขณะที่กล่องขาออกว่างคุณต้องใช้เวลาเชิงเส้นเพื่อไปที่ด้านล่างของกล่องจดหมาย (ดังที่คุณเห็น ในรหัสของเดฟ)
คุณสามารถใช้คิวเป็นรายการที่เชื่อมโยงโดยลำพัง (แต่ละองค์ประกอบชี้ไปที่องค์ประกอบที่แทรกถัดไป) ทำให้ตัวชี้พิเศษไปยังองค์ประกอบแทรกล่าสุดสำหรับการผลักดัน (หรือทำให้มันเป็นรายการวงจร) การใช้คิวและ dequeue ในโครงสร้างข้อมูลนี้ทำได้ง่ายมากในเวลาคงที่ นั่นคือเวลาคงที่ที่เลวร้ายที่สุดไม่ใช่ค่าตัดจำหน่าย และเนื่องจากความคิดเห็นที่ดูเหมือนจะขอคำชี้แจงนี้เวลาคงที่ที่เลวร้ายที่สุดกรณีดีกว่าเวลาคงที่ตัดจำหน่ายอย่างเคร่งครัด
ให้คิวที่จะนำมาใช้เป็น q และกองที่ใช้ในการดำเนินการ q เป็น stack1 และ stack2
q สามารถดำเนินการได้สองวิธี:
วิธีที่ 1 (โดยทำให้การดำเนินการ enQueue มีราคาแพง)
วิธีการนี้ทำให้แน่ใจได้ว่าองค์ประกอบที่เพิ่งป้อนเข้ามาจะอยู่ที่ด้านบนสุดของสแต็ก 1 เสมอดังนั้นการดำเนินการ deQueue จะปรากฏขึ้นจากสแต็ก 1 ในการวางองค์ประกอบที่ด้านบนของ stack1 จะใช้ stack2
enQueue(q, x)
1) While stack1 is not empty, push everything from stack1 to stack2.
2) Push x to stack1 (assuming size of stacks is unlimited).
3) Push everything back to stack1.
deQueue(q)
1) If stack1 is empty then error
2) Pop an item from stack1 and return it.
วิธีที่ 2 (โดยทำให้การดำเนินการ deQueue แพง)
ในวิธีนี้ในการดำเนินการคิวองค์ประกอบใหม่จะถูกป้อนที่ด้านบนของสแต็ค 1 ในการดำเนินการยกเลิกคิวหาก stack2 ว่างเปล่าจากนั้นองค์ประกอบทั้งหมดจะถูกย้ายไปที่ stack2 และในที่สุดก็กลับมาด้านบนของ stack2
enQueue(q, x)
1) Push x to stack1 (assuming size of stacks is unlimited).
deQueue(q)
1) If both stacks are empty then error.
2) If stack2 is empty
While stack1 is not empty, push everything from stack1 to stack2.
3) Pop the element from stack2 and return it.
วิธีที่ 2 ดีกว่าวิธีที่ 1 แน่นอนวิธีที่ 1 ย้ายองค์ประกอบทั้งหมดสองครั้งในการดำเนินการ enQueue ในขณะที่วิธีที่ 2 (ในการดำเนินการ deQueue) จะย้ายองค์ประกอบหนึ่งครั้งและย้ายองค์ประกอบเฉพาะเมื่อ stack2 ว่างเปล่า
ทางออกใน c #
public class Queue<T> where T : class
{
private Stack<T> input = new Stack<T>();
private Stack<T> output = new Stack<T>();
public void Enqueue(T t)
{
input.Push(t);
}
public T Dequeue()
{
if (output.Count == 0)
{
while (input.Count != 0)
{
output.Push(input.Pop());
}
}
return output.Pop();
}
}
สองกองในคิวจะถูกกำหนดเป็นstack1และstack2
Enqueue: องค์ประกอบที่มีค่าจะถูกส่งไปยังstack1 เสมอ
Dequeue: ด้านบนของstack2สามารถถูกเปิดออกเนื่องจากมันเป็นองค์ประกอบแรกที่ใส่เข้าไปในคิวเมื่อstack2ไม่ว่างเปล่า เมื่อstack2ว่างเปล่าเราป๊อปอัพองค์ประกอบทั้งหมดจากstack1และกดลงในstack2หนึ่งต่อหนึ่ง อิลิเมนต์แรกในคิวถูกส่งไปที่ด้านล่างของสแต็ก 1 มันสามารถโผล่ออกมาได้โดยตรงหลังจากที่กดและกดการดำเนินการเพราะมันอยู่ด้านบนของสแต็ค 2
ต่อไปนี้เป็นรหัสตัวอย่าง C ++ เดียวกัน:
template <typename T> class CQueue
{
public:
CQueue(void);
~CQueue(void);
void appendTail(const T& node);
T deleteHead();
private:
stack<T> stack1;
stack<T> stack2;
};
template<typename T> void CQueue<T>::appendTail(const T& element) {
stack1.push(element);
}
template<typename T> T CQueue<T>::deleteHead() {
if(stack2.size()<= 0) {
while(stack1.size()>0) {
T& data = stack1.top();
stack1.pop();
stack2.push(data);
}
}
if(stack2.size() == 0)
throw new exception("queue is empty");
T head = stack2.top();
stack2.pop();
return head;
}
การแก้ปัญหานี้จะถูกยืมมาจากบล็อกของฉัน การวิเคราะห์อย่างละเอียดยิ่งขึ้นด้วยการจำลองการทำงานทีละขั้นตอนมีอยู่ในหน้าเว็บบล็อกของฉัน
คุณจะต้องโผล่ทุกอย่างออกจากสแต็กแรกเพื่อให้ได้องค์ประกอบด้านล่าง จากนั้นนำทั้งหมดกลับสู่สแต็กที่สองสำหรับการดำเนินการ "dequeue" ทุกครั้ง
สำหรับนักพัฒนา c # ที่นี่เป็นโปรแกรมที่สมบูรณ์:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace QueueImplimentationUsingStack
{
class Program
{
public class Stack<T>
{
public int size;
public Node<T> head;
public void Push(T data)
{
Node<T> node = new Node<T>();
node.data = data;
if (head == null)
head = node;
else
{
node.link = head;
head = node;
}
size++;
Display();
}
public Node<T> Pop()
{
if (head == null)
return null;
else
{
Node<T> temp = head;
//temp.link = null;
head = head.link;
size--;
Display();
return temp;
}
}
public void Display()
{
if (size == 0)
Console.WriteLine("Empty");
else
{
Console.Clear();
Node<T> temp = head;
while (temp!= null)
{
Console.WriteLine(temp.data);
temp = temp.link;
}
}
}
}
public class Queue<T>
{
public int size;
public Stack<T> inbox;
public Stack<T> outbox;
public Queue()
{
inbox = new Stack<T>();
outbox = new Stack<T>();
}
public void EnQueue(T data)
{
inbox.Push(data);
size++;
}
public Node<T> DeQueue()
{
if (outbox.size == 0)
{
while (inbox.size != 0)
{
outbox.Push(inbox.Pop().data);
}
}
Node<T> temp = new Node<T>();
if (outbox.size != 0)
{
temp = outbox.Pop();
size--;
}
return temp;
}
}
public class Node<T>
{
public T data;
public Node<T> link;
}
static void Main(string[] args)
{
Queue<int> q = new Queue<int>();
for (int i = 1; i <= 3; i++)
q.EnQueue(i);
// q.Display();
for (int i = 1; i < 3; i++)
q.DeQueue();
//q.Display();
Console.ReadKey();
}
}
}
ใช้การดำเนินการของคิวต่อไปนี้โดยใช้สแต็ก
push (x) - ผลักองค์ประกอบ x ไปด้านหลังของคิว
pop () - ลบองค์ประกอบออกจากด้านหน้าของคิว
peek () - รับองค์ประกอบด้านหน้า
empty () - ส่งคืนว่าคิวว่างเปล่าหรือไม่
class MyQueue {
Stack<Integer> input;
Stack<Integer> output;
/** Initialize your data structure here. */
public MyQueue() {
input = new Stack<Integer>();
output = new Stack<Integer>();
}
/** Push element x to the back of queue. */
public void push(int x) {
input.push(x);
}
/** Removes the element from in front of queue and returns that element. */
public int pop() {
peek();
return output.pop();
}
/** Get the front element. */
public int peek() {
if(output.isEmpty()) {
while(!input.isEmpty()) {
output.push(input.pop());
}
}
return output.peek();
}
/** Returns whether the queue is empty. */
public boolean empty() {
return input.isEmpty() && output.isEmpty();
}
}
// Two stacks s1 Original and s2 as Temp one
private Stack<Integer> s1 = new Stack<Integer>();
private Stack<Integer> s2 = new Stack<Integer>();
/*
* Here we insert the data into the stack and if data all ready exist on
* stack than we copy the entire stack s1 to s2 recursively and push the new
* element data onto s1 and than again recursively call the s2 to pop on s1.
*
* Note here we can use either way ie We can keep pushing on s1 and than
* while popping we can remove the first element from s2 by copying
* recursively the data and removing the first index element.
*/
public void insert( int data )
{
if( s1.size() == 0 )
{
s1.push( data );
}
else
{
while( !s1.isEmpty() )
{
s2.push( s1.pop() );
}
s1.push( data );
while( !s2.isEmpty() )
{
s1.push( s2.pop() );
}
}
}
public void remove()
{
if( s1.isEmpty() )
{
System.out.println( "Empty" );
}
else
{
s1.pop();
}
}
การใช้งานคิวโดยใช้สองกองใน Swift:
struct Stack<Element> {
var items = [Element]()
var count : Int {
return items.count
}
mutating func push(_ item: Element) {
items.append(item)
}
mutating func pop() -> Element? {
return items.removeLast()
}
func peek() -> Element? {
return items.last
}
}
struct Queue<Element> {
var inStack = Stack<Element>()
var outStack = Stack<Element>()
mutating func enqueue(_ item: Element) {
inStack.push(item)
}
mutating func dequeue() -> Element? {
fillOutStack()
return outStack.pop()
}
mutating func peek() -> Element? {
fillOutStack()
return outStack.peek()
}
private mutating func fillOutStack() {
if outStack.count == 0 {
while inStack.count != 0 {
outStack.push(inStack.pop()!)
}
}
}
}
ในขณะที่คุณจะได้รับการโพสต์จำนวนมากที่เกี่ยวข้องกับการใช้คิวที่มีสองกอง: 1. โดยการทำให้กระบวนการ enQueue มีค่าใช้จ่ายมากขึ้น 2. หรือโดยการทำให้กระบวนการ deQueue มีราคาแพงมากขึ้น
https://www.geeksforgeeks.org/queue-using-stacks/
วิธีหนึ่งที่สำคัญที่ฉันค้นพบจากโพสต์ด้านบนคือการสร้างคิวด้วยโครงสร้างข้อมูลสแต็กเท่านั้นและสแต็กการเรียกซ้ำ
ในขณะที่หนึ่งสามารถยืนยันว่าแท้จริงนี้ยังคงใช้สองสแต็ก แต่แล้วความนึกคิดนี้ใช้เพียงหนึ่งโครงสร้างข้อมูลสแต็ค
ด้านล่างนี้เป็นคำอธิบายของปัญหา:
ประกาศสแต็กเดี่ยวสำหรับ enQueuing และ deQueing ข้อมูลแล้วพุชข้อมูลลงในสแต็ก
ในขณะที่ deQueueing มีสภาพพื้นฐานที่องค์ประกอบของสแต็คจะปรากฏขึ้นเมื่อขนาดของสแต็คเป็น 1 เพื่อให้แน่ใจว่าไม่มีการล้นสแต็คในระหว่างการเรียกซ้ำ deQueue
ขณะที่ deQueueing ปรากฏข้อมูลจากด้านบนสุดของสแต็ก โดยหลักการแล้วองค์ประกอบนี้จะเป็นองค์ประกอบที่มีอยู่ที่ด้านบนสุดของสแต็ก ทีนี้เมื่อทำเสร็จแล้วให้เรียกใช้ฟังก์ชัน deQueue ซ้ำแล้วดันองค์ประกอบที่โผล่ขึ้นมากลับเข้าไปในกองซ้อน
รหัสจะมีลักษณะดังนี้:
if (s1.isEmpty())
System.out.println("The Queue is empty");
else if (s1.size() == 1)
return s1.pop();
else {
int x = s1.pop();
int result = deQueue();
s1.push(x);
return result;
วิธีนี้คุณสามารถสร้างคิวโดยใช้โครงสร้างข้อมูลสแต็กเดียวและกองการเรียกซ้ำ
ด้านล่างเป็นวิธีแก้ปัญหาในภาษาจาวาสคริปต์โดยใช้ไวยากรณ์ ES6
Stack.js
//stack using array
class Stack {
constructor() {
this.data = [];
}
push(data) {
this.data.push(data);
}
pop() {
return this.data.pop();
}
peek() {
return this.data[this.data.length - 1];
}
size(){
return this.data.length;
}
}
export { Stack };
QueueUsingTwoStacks.js
import { Stack } from "./Stack";
class QueueUsingTwoStacks {
constructor() {
this.stack1 = new Stack();
this.stack2 = new Stack();
}
enqueue(data) {
this.stack1.push(data);
}
dequeue() {
//if both stacks are empty, return undefined
if (this.stack1.size() === 0 && this.stack2.size() === 0)
return undefined;
//if stack2 is empty, pop all elements from stack1 to stack2 till stack1 is empty
if (this.stack2.size() === 0) {
while (this.stack1.size() !== 0) {
this.stack2.push(this.stack1.pop());
}
}
//pop and return the element from stack 2
return this.stack2.pop();
}
}
export { QueueUsingTwoStacks };
ด้านล่างคือการใช้งาน:
index.js
import { StackUsingTwoQueues } from './StackUsingTwoQueues';
let que = new QueueUsingTwoStacks();
que.enqueue("A");
que.enqueue("B");
que.enqueue("C");
console.log(que.dequeue()); //output: "A"
stack1
นั้น เมื่อคุณไปที่dequeue
อีกครั้งคุณจะย้ายรายการstack2
เหล่านั้นไปไว้ข้างหน้าโดยวางสิ่งที่มีอยู่แล้ว
ฉันจะตอบคำถามนี้ใน Go เพราะ Go ไม่มีคอลเลกชันมากมายในไลบรารีมาตรฐาน
เนื่องจากสแต็กนั้นใช้งานได้ง่ายมากฉันคิดว่าฉันลองและใช้สแต็คสองตัวเพื่อให้ได้คิวคู่ที่สิ้นสุด เพื่อให้เข้าใจได้ดียิ่งขึ้นว่าฉันมาถึงคำตอบได้อย่างไรฉันได้แบ่งการใช้งานเป็นสองส่วนส่วนแรกคือหวังว่าจะเข้าใจได้ง่ายขึ้น แต่ก็ไม่สมบูรณ์
type IntQueue struct {
front []int
back []int
}
func (q *IntQueue) PushFront(v int) {
q.front = append(q.front, v)
}
func (q *IntQueue) Front() int {
if len(q.front) > 0 {
return q.front[len(q.front)-1]
} else {
return q.back[0]
}
}
func (q *IntQueue) PopFront() {
if len(q.front) > 0 {
q.front = q.front[:len(q.front)-1]
} else {
q.back = q.back[1:]
}
}
func (q *IntQueue) PushBack(v int) {
q.back = append(q.back, v)
}
func (q *IntQueue) Back() int {
if len(q.back) > 0 {
return q.back[len(q.back)-1]
} else {
return q.front[0]
}
}
func (q *IntQueue) PopBack() {
if len(q.back) > 0 {
q.back = q.back[:len(q.back)-1]
} else {
q.front = q.front[1:]
}
}
เป็นพื้นสองกองที่เราอนุญาตให้ด้านล่างของสแต็คได้รับการจัดการซึ่งกันและกัน ฉันยังใช้การตั้งชื่อแบบแผนการประชุมที่การดำเนินการแบบพุชแบบป๊อปและแบบแอบมองของกองซ้อนมีคำนำหน้า / หลังไม่ว่าพวกเขาจะอ้างถึงด้านหน้าหรือด้านหลังของคิว
ปัญหาของโค้ดข้างต้นคือมันไม่ได้ใช้หน่วยความจำอย่างมีประสิทธิภาพ ที่จริงแล้วมันจะเติบโตอย่างไม่มีที่สิ้นสุดจนกว่าคุณจะหมดพื้นที่ มันแย่มากจริงๆ การแก้ไขสำหรับสิ่งนี้คือการใช้ซ้ำด้านล่างของพื้นที่สแต็คเมื่อทำได้ เราต้องแนะนำออฟเซ็ตเพื่อติดตามสิ่งนี้เนื่องจากชิ้นส่วนในโกไม่สามารถเติบโตได้ด้านหน้าเมื่อหดตัวลง
type IntQueue struct {
front []int
frontOffset int
back []int
backOffset int
}
func (q *IntQueue) PushFront(v int) {
if q.backOffset > 0 {
i := q.backOffset - 1
q.back[i] = v
q.backOffset = i
} else {
q.front = append(q.front, v)
}
}
func (q *IntQueue) Front() int {
if len(q.front) > 0 {
return q.front[len(q.front)-1]
} else {
return q.back[q.backOffset]
}
}
func (q *IntQueue) PopFront() {
if len(q.front) > 0 {
q.front = q.front[:len(q.front)-1]
} else {
if len(q.back) > 0 {
q.backOffset++
} else {
panic("Cannot pop front of empty queue.")
}
}
}
func (q *IntQueue) PushBack(v int) {
if q.frontOffset > 0 {
i := q.frontOffset - 1
q.front[i] = v
q.frontOffset = i
} else {
q.back = append(q.back, v)
}
}
func (q *IntQueue) Back() int {
if len(q.back) > 0 {
return q.back[len(q.back)-1]
} else {
return q.front[q.frontOffset]
}
}
func (q *IntQueue) PopBack() {
if len(q.back) > 0 {
q.back = q.back[:len(q.back)-1]
} else {
if len(q.front) > 0 {
q.frontOffset++
} else {
panic("Cannot pop back of empty queue.")
}
}
}
มันมีฟังก์ชั่นเล็ก ๆ มากมาย แต่ฟังก์ชั่นทั้ง 6 ฟังก์ชั่น 3 ตัวนั้นเป็นเพียงแค่ฟังก์ชั่นอื่น ๆ
นี่คือทางออกของฉันใน java โดยใช้ linkedlist
class queue<T>{
static class Node<T>{
private T data;
private Node<T> next;
Node(T data){
this.data = data;
next = null;
}
}
Node firstTop;
Node secondTop;
void push(T data){
Node temp = new Node(data);
temp.next = firstTop;
firstTop = temp;
}
void pop(){
if(firstTop == null){
return;
}
Node temp = firstTop;
while(temp != null){
Node temp1 = new Node(temp.data);
temp1.next = secondTop;
secondTop = temp1;
temp = temp.next;
}
secondTop = secondTop.next;
firstTop = null;
while(secondTop != null){
Node temp3 = new Node(secondTop.data);
temp3.next = firstTop;
firstTop = temp3;
secondTop = secondTop.next;
}
}
}
หมายเหตุ:ในกรณีนี้การดำเนินการแบบ pop ใช้เวลานานมาก ดังนั้นฉันจะไม่แนะนำให้สร้างคิวโดยใช้สองกอง
ด้วยO(1)
dequeue()
ซึ่งเหมือนกับคำตอบของ pythonquick :
// time: O(n), space: O(n)
enqueue(x):
if stack.isEmpty():
stack.push(x)
return
temp = stack.pop()
enqueue(x)
stack.push(temp)
// time: O(1)
x dequeue():
return stack.pop()
ด้วยO(1)
enqueue()
(ไม่ได้กล่าวถึงในโพสต์นี้ดังนั้นคำตอบนี้) ซึ่งใช้การย้อนรอยเพื่อทำให้ฟองสบู่ขึ้นและส่งคืนรายการที่ต่ำที่สุด
// O(1)
enqueue(x):
stack.push(x)
// time: O(n), space: O(n)
x dequeue():
temp = stack.pop()
if stack.isEmpty():
x = temp
else:
x = dequeue()
stack.push(temp)
return x
เห็นได้ชัดว่ามันเป็นแบบฝึกหัดที่ดีเนื่องจากไม่มีประสิทธิภาพ แต่ก็ยังสวยงาม
** โซลูชัน JS ง่าย ๆ **
/*
enQueue(q, x)
1) Push x to stack1 (assuming size of stacks is unlimited).
deQueue(q)
1) If both stacks are empty then error.
2) If stack2 is empty
While stack1 is not empty, push everything from stack1 to stack2.
3) Pop the element from stack2 and return it.
*/
class myQueue {
constructor() {
this.stack1 = [];
this.stack2 = [];
}
push(item) {
this.stack1.push(item)
}
remove() {
if (this.stack1.length == 0 && this.stack2.length == 0) {
return "Stack are empty"
}
if (this.stack2.length == 0) {
while (this.stack1.length != 0) {
this.stack2.push(this.stack1.pop())
}
}
return this.stack2.pop()
}
peek() {
if (this.stack2.length == 0 && this.stack1.length == 0) {
return 'Empty list'
}
if (this.stack2.length == 0) {
while (this.stack1.length != 0) {
this.stack2.push(this.stack1.pop())
}
}
return this.stack2[0]
}
isEmpty() {
return this.stack2.length === 0 && this.stack1.length === 0;
}
}
const q = new myQueue();
q.push(1);
q.push(2);
q.push(3);
q.remove()
console.log(q)
public class QueueUsingStacks<T>
{
private LinkedListStack<T> stack1;
private LinkedListStack<T> stack2;
public QueueUsingStacks()
{
stack1=new LinkedListStack<T>();
stack2 = new LinkedListStack<T>();
}
public void Copy(LinkedListStack<T> source,LinkedListStack<T> dest )
{
while(source.Head!=null)
{
dest.Push(source.Head.Data);
source.Head = source.Head.Next;
}
}
public void Enqueue(T entry)
{
stack1.Push(entry);
}
public T Dequeue()
{
T obj;
if (stack2 != null)
{
Copy(stack1, stack2);
obj = stack2.Pop();
Copy(stack2, stack1);
}
else
{
throw new Exception("Stack is empty");
}
return obj;
}
public void Display()
{
stack1.Display();
}
}
สำหรับการดำเนินงานคิวงานทุกครั้งเราเพิ่มไปที่ด้านบนสุดของสแต็ก 1 สำหรับ dequeue ทุกครั้งเราจะลบเนื้อหาของ stack1 ลงใน stack2 และลบองค์ประกอบที่อยู่ด้านบนของ stack ความซับซ้อนของเวลาคือ O (n) สำหรับ dequeue เนื่องจากเราต้องคัดลอก stack1 ไปยัง stack2 ความซับซ้อนของเวลาของการเข้าคิวเป็นเหมือนสแต็กปกติ
if (stack2 != null)
เป็นจริงเสมอเนื่องจากstack2
มีการสร้างอินสแตนซ์ในตัวสร้าง
การใช้คิวในการใช้วัตถุ java.util.Stack สองอัน:
public final class QueueUsingStacks<E> {
private final Stack<E> iStack = new Stack<>();
private final Stack<E> oStack = new Stack<>();
public void enqueue(E e) {
iStack.push(e);
}
public E dequeue() {
if (oStack.isEmpty()) {
if (iStack.isEmpty()) {
throw new NoSuchElementException("No elements present in Queue");
}
while (!iStack.isEmpty()) {
oStack.push(iStack.pop());
}
}
return oStack.pop();
}
public boolean isEmpty() {
if (oStack.isEmpty() && iStack.isEmpty()) {
return true;
}
return false;
}
public int size() {
return iStack.size() + oStack.size();
}
}
return inbox.isEmpty() && outbox.isEmpty()
และreturn inbox.size() + outbox.size()
ตามลำดับ รหัสของ Dave L. มีข้อผิดพลาดอยู่แล้วเมื่อคุณ dequeue จากคิวที่ว่างเปล่า คำถามเดิมไม่ได้เกี่ยวกับ Java มันเป็นเรื่องของโครงสร้างข้อมูล / อัลกอริทึมโดยทั่วไป การนำ Java ไปใช้นั้นเป็นเพียงภาพประกอบเพิ่มเติม