답변:
스택 2 개를 유지 inbox
하고로 호출합니다 outbox
.
대기열 :
inbox
대기열에서 제외 :
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}
. 동일한 입력에 대해 큐가 비워 질 때까지 큐를 대기열에서 빼면 출력이됩니다. 될 것 {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}
각각 대기열에 넣습니다 . 정수는 왼쪽 에있는 입력 스택 ( 스택 # 1 ) 에서 푸시됩니다 .
그런 다음 대기열 제거 작업을 실행하면 어떻게됩니까? 큐 제거 작업이 실행될 때마다 큐는 출력 스택이 비어 있는지 확인합니다 (위의 의사 코드 참조) 출력 스택이 비어 있으면 출력에서 입력 스택이 추출되어 요소가 추출됩니다. 입력 스택이 반대로됩니다. 값을 반환하기 전에 대기열의 상태는 다음과 같습니다.
출력 스택 (스택 # 2)에서 요소의 순서를 확인하십시오. 출력 스택에서 요소를 팝업하여 출력이 대기열에서 대기열에서 제외 된 것과 동일하게되도록 할 수 있습니다. 따라서 두 개의 대기열 제거 작업을 실행하면 먼저 얻을 것 {1, 2}
입니다. 그러면 요소 3이 출력 스택의 유일한 요소가되고 입력 스택이 비어있게됩니다. 요소 4와 5를 큐에 넣으면 큐의 상태는 다음과 같습니다.
이제 출력 스택이 비어 있지 않으며, dequeue 작업을 실행하면 출력 스택에서 3 개만 튀어 나옵니다. 그러면 상태는 다음과 같습니다.
다시 말하지만, 두 개의 추가 대기열 제거 작업을 실행하면 첫 번째 대기열 제거 작업에서 대기열이 출력 스택이 비어 있는지 확인합니다. 그런 다음 입력 스택의 요소를 꺼내서 입력 스택이 비어있을 때까지 출력 스택으로 밀어 넣습니다. 그러면 대기열의 상태는 다음과 같습니다.
알기 쉽게, 두 대기열 제거 작업의 출력은 {4, 5}
다음은 Java로 구현 한 것입니다. 나는 기존의 Stack 구현을 사용하지 않을 것이므로 여기 예제는 바퀴를 재발 명 할 것입니다.
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
하나의 스택 만 사용하여 대기열을 시뮬레이션 할 수도 있습니다. 두 번째 (임시) 스택은 insert 메소드에 대한 재귀 호출의 호출 스택으로 시뮬레이션 할 수 있습니다.
대기열에 새 요소를 삽입 할 때 원칙은 동일하게 유지됩니다.
하나의 스택 만 사용하는 큐 클래스는 다음과 같습니다.
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
위의 데이터 구조를 사용하여 큐 에 삽입하는 데 2 차 시간 O (n ^ 2)가 걸립니다 . 합계 (1 + 2 + 4 + 8 + .... + 2(n-1))
는 ~O(n^2)
입니다. 나는 당신이 요점을 얻길 바랍니다.
그러나 시간 복잡성은 더 나쁠 것입니다. 좋은 큐 구현은 모든 것을 일정한 시간에 수행합니다.
편집하다
왜 내 대답이 다운 다운되었는지 확실하지 않습니다. 프로그래밍하는 경우 시간 복잡성을 염두에두고 대기열을 만들기 위해 두 개의 표준 스택을 사용하는 것은 비효율적입니다. 매우 타당하고 관련성이 높은 요점입니다. 다른 사람이 이것을 더 공감해야 할 필요가 있다고 생각하면 이유를 알고 싶습니다.
좀 더 자세한 내용 : 두 개의 스택을 사용하는 것이 대기열보다 나쁜 이유 : 두 개의 스택을 사용하고 보낼 편지함이 비어있는 동안 누군가 대기열에서 전화를 걸면받은 편지함의 맨 아래에 도착하는 데 선형 시간이 필요합니다 (보시다시피) Dave의 코드에서).
큐를 단일 링크 목록 (각 요소가 다음에 삽입 된 요소를 가리킴)으로 구현하여 푸시를 위해 마지막으로 삽입 된 요소에 대한 추가 포인터를 유지하거나 순환 목록으로 만들 수 있습니다. 이 데이터 구조에서 큐 및 큐를 구현하는 것은 일정한 시간에 수행하기가 매우 쉽습니다. 그것은 최악의 일정한 시간이며 상각되지 않습니다. 그리고 의견 이이 설명을 요구하는 것처럼 보이는 최악의 일정 시간은 상각 일정 시간보다 엄격하게 좋습니다.
큐를 q로 구현하고 q를 구현하는 데 사용 된 스택을 stack1 및 stack2로 둡니다.
q는 두 가지 방법 으로 구현 될 수 있습니다 .
방법 1 (enQueue 작업 비용이 많이 들음)
이 메소드는 새로 입력 한 요소가 항상 스택 1의 맨 위에 오도록하여 deQueue 작업이 stack1에서 시작되도록합니다. 요소를 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 작업 비용이 많이 들음)
이 방법에서는 대기열에 넣기 작업에서 새 요소가 stack1의 맨 위에 입력됩니다. 대기열에서 제외 작업에서 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 .
대기열에 추가 : 대기열에 추가 된 요소는 항상 stack1 로 푸시됩니다 .
Dequeue : stack2 가 비어 있지 않은 경우 queue2 가 큐에 삽입 된 첫 번째 요소이므로 stack2 의 상단 이 튀어 나올 수 있습니다 . 때 stack2가 비어 있습니다, 우리는 모든 요소 팝업 stack1를 하고로 밀어 stack2 하나 하나. 큐의 첫 번째 요소는 stack1 의 맨 아래로 푸시됩니다 . 스택 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/
위의 게시물에서 찾은 중요한 한 가지 방법은 스택 데이터 구조와 재귀 호출 스택 만 사용하여 대기열을 구성하는 것입니다.
말 그대로 이것은 여전히 두 개의 스택을 사용하고 있지만 이상적으로는 하나의 스택 데이터 구조 만 사용하는 것이 좋습니다.
아래는 문제에 대한 설명입니다.
데이터 대기열 처리 및 대기열 해제를 위해 단일 스택을 선언하고 데이터를 스택에 넣습니다.
deQueueing은 스택의 크기가 1 일 때 스택 요소가 팝업되는 기본 조건을 갖습니다. 이렇게하면 deQueue 재귀 중에 스택 오버플로가 발생하지 않습니다.
대기열에서 대기하는 동안 먼저 스택 맨 위에서 데이터를 팝합니다. 이상적으로이 요소는 스택 맨 위에있는 요소입니다. 이 작업이 완료되면 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:]
}
}
기본적으로 스택의 하단을 서로 조작 할 수있는 두 개의 스택입니다. 또한 스택의 전통적인 푸시, 팝, 엿보기 작업이 대기열의 앞면 또는 뒷면을 참조하든 앞 / 뒤 접두사가있는 STL 명명 규칙을 사용했습니다.
위 코드의 문제점은 메모리를 매우 효율적으로 사용하지 않는다는 것입니다. 실제로 공간이 부족해질 때까지 끝없이 자랍니다. 정말 나쁘다. 이를위한 해결책은 가능할 때마다 스택 공간의 맨 아래를 재사용하는 것입니다. Go의 슬라이스가 한 번 축소 된 후에는 커질 수 없으므로이를 추적하기 위해 오프셋을 도입해야합니다.
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 개는 다른 것의 거울 일뿐입니다.
다음은 linkedlist를 사용하는 Java의 솔루션입니다.
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;
}
}
}
참고 : 이 경우 팝 작업에는 시간이 많이 걸립니다. 따라서 두 스택을 사용하여 대기열을 만들 것을 제안하지 않습니다.
로 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();
}
}
모든 대기열 작업에 대해 stack1의 맨 위에 추가합니다. 모든 대기열에서 스택 1의 내용을 스택 2에 비우고 스택의 맨 위에있는 요소를 제거합니다. 스택 1을 스택 2에 복사해야하므로 대기열에서 시간 복잡도는 O (n)입니다. 대기열의 시간 복잡도는 일반 스택과 동일
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.의 코드에서 이미 예외가 발생합니다. 원래 질문은 Java에 대한 것이 아닙니다. 그것은 일반적으로 데이터 구조 / 알고리즘에 관한 것입니다. Java 구현은 추가 설명 일뿐입니다.