자료구조

[자료구조] Stack, Queue 그리고 Deque

Jaemin0604 2024. 7. 3. 16:55

Stack, Queue, Deque

스택(Stack)

  • 박스 쌓기에 비유할 수 있다.
  • 선입후출(FILO) 구조 또는 후입선출 구조

  • 스택의 메서드

  • 대기 줄에 비유할 수 있다.
  • 선입선출(FIFO) 구조

  • 큐의 매서드

 

간단한 스택과 큐 구현

public static void main(String[] args) { 
    Stack st = new Stack(); 
    Queue q = new LinkedList();

    st.push("0");
    st.push("1");
    st.push("2");
    q.offer("0");
    q.offer("1");
    q.offer("2");

    while(!st.isEmpty()) {
        System.out.print(st.pop());
    }

    while(!q.isEmpty()) {
        System.out.print(q.poll());
    }
}

실행결과 : 210 012

 

Deque(Double-Ended Queue)

Deque는 양쪽 끝에서 추가와 삭제가 가능하다.

Java공식문서 표 참조

  • 개념: 덱(Deque)은 양쪽 끝에서 원소를 추가하거나 삭제할 수 있는 자료 구조
  • 인터페이스: Java에서는 Deque를 인터페이스로 제공하며, 다양한 구현체가 있습니다. 주요 메서드로는 addFirst(), addLast(), removeFirst(), removeLast(), getFirst(), getLast() 등이 있습니다.
  • 기능: 스택(Stack)과 큐(Queue)의 모든 기능을 지원, 양쪽 끝에서의 원소 추가/삭제가 O(1) 시간 복잡도로 가능
  • 구현체: Java에서는 ArrayDeque 외에도 LinkedList도 Deque 인터페이스를 구현하여 사용할 수 있다.

Stack VS ArrayDeque

자바로 스택을 구현할 때에는 주로 ArrayDeque을 이용한다.

Stack이 상속하는 Vector의 문제점

Stack이 상속하는 Vector의 가장 큰 문제점은 주요 메서드들이 각각 동기화를 진행한다는 것이다.

// add, addElement, get 등 주요 public 메서드들이 synchronized 키워드를 달고 있다.
public synchronized boolean add(E e) {
    modCount++;
    add(e, elementData, elementCount);
    return true;
}
Vector<Integer> vector = new Vector<>();
for (int i = 0; i < 100_000_000; i++) {
    vector.add(i);
} // 3484ms

ArrayList<Integer> arrayList = new ArrayList<>();
for (int i = 0; i < 100_000_000; i++) {
    arrayList.add(i);
} // 2319ms

 

  • 거의 1.5배의 속도 차이가 발생하며 이는 vector의 동기화 작업 때문일 것으로 예상된다.
  • Stack은 Vector의 메서드를 그대로 사용하며 동기화 관련 문제가 그대로 발생한다.

ArrayDeque는 불필요한 동기화 없이 offerLast 메서드와 pollLast 메서드로 Stack의 기능을 완벽히 대체할 수 있다.

 

Queue VS ArrayDeque

Queue(LinkedList) ArrayDeque
이중 연결 리스트로 구현 크기가 조정 가능한 배열로 구현
각 요소는 데이터와 두 개의 포인터(이전 요소와 다음 요소를 가리킴)를 포함하므로, 추가 메모리 사용 배열을 사용하므로, LinkedList 보다 메모리 오버헤드가 적다.
하지만 배열이 꽉 차면 새로운 배열을 할당하고 기존 요소를 복사해야 한다.
리스트의 양 끝에서 요소를 삽입하거나 삭제하는 작업이 빠르다(O(1)). 중간 요소의 삽입/삭제도 포인터 조작만으로 처리되므로 효율적이다. 배열의 양 끝에서 요소를 삽입하거나 삭제하는 작업이 빠르다(O(1)). 단, 배열 크기를 조정하는 경우 성능이 저하될 수 있다.
특정 인덱스의 요소에 접근하는 시간이 O(n)으로, 리스트의 크기가 커질수록 성능이 저하된다. 특정 인덱스의 요소에 접근하는 시간이 O(1)이다.

 

ArrayList vs ArrayDeque vs LinkedList??

  • 인덱스로 데이터에 접근하고 끝에 삽입, 삭제만 할 경우에는 ArrayList를 사용
  • stack, queue, 혹은 deque로 ArrayDeque를 사용
  • 리스트를 순회할때 삽입, 삭제하거나 O(1)인 최악의 경우에 마지막에 삽입시 LinkedList를 사용